aipctl 2026.8.5 → 2026.9.1

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/README.md CHANGED
@@ -34,6 +34,29 @@ aipctl tunnel link <token>
34
34
 
35
35
  `install`, `run`, `tunnel`, `logs`는 Go native binary로 lazy dispatch됩니다. `prod` build에서는 `logs` command가 help와 command tree에서 제외됩니다.
36
36
 
37
+ ## Experimental model execution
38
+
39
+ `aipctl model-exec`은 CLI 전용 one-shot 모델 실행 namespace입니다. Conversation을 만들지 않고, 사용자가 선택한 모델을 한 번 호출해 stdout 또는 파일로 결과를 받습니다.
40
+
41
+ ```bash
42
+ aipctl model-exec models [--type chat|image-generation] [--json]
43
+ aipctl model-exec get <model-id> [--json]
44
+ aipctl model-exec sample <model-id> [--save input.json]
45
+ aipctl model-exec chat --model <chat-model-id> -m "hello" [--json]
46
+ aipctl model-exec image generate --model <image-model-id> --prompt "cat" --output ./cat.png [--json]
47
+ ```
48
+
49
+ 대화형 터미널에서는 `model-exec chat`과 `model-exec image generate`가 완료될 때까지 spinner와 경과 시간을 stderr에 표시합니다. JSON을 포함한 최종 결과는 기존처럼 stdout에만 출력됩니다.
50
+
51
+ `model-exec image generate`의 이미지 생성 요청 timeout은 최대 5분입니다. 5분 안에 provider 응답이 완료되지 않으면 명령은 실패하며, 이 제한은 one-shot image-generation 요청에만 적용됩니다. `aipctl model-exec image generate --help`와 실행 중 spinner에서도 제한을 확인할 수 있습니다.
52
+
53
+ Conversation을 유지하는 일반 채팅은 `chat send`를 사용합니다.
54
+
55
+ ```bash
56
+ aipctl chat send -m "hello" --json
57
+ aipctl chat send --conversation <conversation-id> -m "continue" --json
58
+ ```
59
+
37
60
  ## Development
38
61
 
39
62
  ```bash
package/build/index.cjs CHANGED
@@ -89666,11 +89666,11 @@ const runMain = runMain$1;
89666
89666
  const BUILD_ENVS = ["local", "dev", "qa", "stage", "prod", "test"];
89667
89667
  const isBuildEnv = (value) => BUILD_ENVS.includes(value);
89668
89668
 
89669
- const injectedBuildEnv = "prod".length > 0 ? "prod" : "local";
89669
+ const injectedBuildEnv = "dev".length > 0 ? "dev" : "local";
89670
89670
  if (!isBuildEnv(injectedBuildEnv)) {
89671
89671
  throw new Error(`Invalid AIPCTL_BUILD_ENV: ${injectedBuildEnv}`);
89672
89672
  }
89673
- const VERSION = "2026.8.5".length > 0 ? "2026.8.5" : "0.0.0";
89673
+ const VERSION = "2026.9.1".length > 0 ? "2026.9.1" : "0.0.0";
89674
89674
  const BUILD_ENV = injectedBuildEnv;
89675
89675
  const INCLUDE_NATIVE_LOGS = BUILD_ENV !== "prod";
89676
89676
 
@@ -94487,6 +94487,30 @@ const ApiServiceLive = effect$1(
94487
94487
  ...res.defaultModel === id ? { isDefault: true } : {}
94488
94488
  }));
94489
94489
  }),
94490
+ listModelExecModels: (orgSlug) => gen(function* () {
94491
+ yield* requireChatBaseUrlEffect();
94492
+ const token = yield* getAccessToken();
94493
+ const path = `/api/${encodeURIComponent(orgSlug)}/cli/model-exec/models`;
94494
+ return yield* makeRequest(chatBaseUrl, path, token);
94495
+ }),
94496
+ executeModelExecChat: (orgSlug, body) => gen(function* () {
94497
+ yield* requireChatBaseUrlEffect();
94498
+ const token = yield* getAccessToken();
94499
+ const path = `/api/${encodeURIComponent(orgSlug)}/cli/model-exec/chat`;
94500
+ return yield* makeRequest(chatBaseUrl, path, token, {
94501
+ method: "POST",
94502
+ body
94503
+ });
94504
+ }),
94505
+ executeModelExecImageGeneration: (orgSlug, body) => gen(function* () {
94506
+ yield* requireChatBaseUrlEffect();
94507
+ const token = yield* getAccessToken();
94508
+ const path = `/api/${encodeURIComponent(orgSlug)}/cli/model-exec/images/generations`;
94509
+ return yield* makeRequest(chatBaseUrl, path, token, {
94510
+ method: "POST",
94511
+ body
94512
+ });
94513
+ }),
94490
94514
  getCreditStatistics: (orgSlug, options) => gen(function* () {
94491
94515
  const token = yield* getAccessToken();
94492
94516
  const params = new URLSearchParams();
@@ -94638,6 +94662,15 @@ const ApiServiceLive = effect$1(
94638
94662
  const path = `/librechat/api/${encodeURIComponent(orgSlug)}/messages/${encodeURIComponent(conversationId)}`;
94639
94663
  return yield* makeRequest(chatBaseUrl, path, token);
94640
94664
  }),
94665
+ sendChatMessage: (orgSlug, body) => gen(function* () {
94666
+ yield* requireChatBaseUrlEffect();
94667
+ const token = yield* getAccessToken();
94668
+ const path = `/api/${encodeURIComponent(orgSlug)}/cli/chats/messages`;
94669
+ return yield* makeRequest(chatBaseUrl, path, token, {
94670
+ method: "POST",
94671
+ body
94672
+ });
94673
+ }),
94641
94674
  // Phase 8: Agent admin CRUD
94642
94675
  createInstalledAgent: (orgSlug, body) => gen(function* () {
94643
94676
  const token = yield* getAccessToken();
@@ -95045,14 +95078,14 @@ function jsonFormat(data, options = {}) {
95045
95078
  }
95046
95079
 
95047
95080
  const agentIdArg$4 = text$2({ name: "id" }).pipe(withDescription$5("Agent ID to export"));
95048
- const outputOption$2 = text("output").pipe(
95081
+ const outputOption$3 = text("output").pipe(
95049
95082
  withAlias("o"),
95050
95083
  withDescription$3("Output file path (defaults to {agentId}.yaml)"),
95051
95084
  optional
95052
95085
  );
95053
95086
  const exportCommand = make$4(
95054
95087
  "export",
95055
- { agentId: agentIdArg$4, org: orgOption, output: outputOption$2 },
95088
+ { agentId: agentIdArg$4, org: orgOption, output: outputOption$3 },
95056
95089
  ({ agentId, org, output }) => gen(function* () {
95057
95090
  const api = yield* ApiService;
95058
95091
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -95245,7 +95278,7 @@ const adminAgentCommand = make$4("agent", {}).pipe(
95245
95278
  withSubcommands(adminAgentSubcommands)
95246
95279
  );
95247
95280
 
95248
- const typeOption = text("type").pipe(
95281
+ const typeOption$1 = text("type").pipe(
95249
95282
  withDescription$3("Filter by event type"),
95250
95283
  optional
95251
95284
  );
@@ -95255,7 +95288,7 @@ const pageOption$3 = integer("page").pipe(
95255
95288
  );
95256
95289
  const listCommand$a = make$4(
95257
95290
  "list",
95258
- { org: orgOption, type: typeOption, page: pageOption$3 },
95291
+ { org: orgOption, type: typeOption$1, page: pageOption$3 },
95259
95292
  ({ org, type, page }) => gen(function* () {
95260
95293
  const api = yield* ApiService;
95261
95294
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -95419,7 +95452,7 @@ const deleteCommand$5 = make$4(
95419
95452
  ).pipe(withDescription("Delete an integration by ID (requires --force to confirm)"));
95420
95453
 
95421
95454
  const integrationIdArg$3 = text$2({ name: "id" }).pipe(withDescription$5("Integration ID"));
95422
- const getCommand$6 = make$4(
95455
+ const getCommand$7 = make$4(
95423
95456
  "get",
95424
95457
  { integrationId: integrationIdArg$3, org: orgOption },
95425
95458
  ({ integrationId, org }) => gen(function* () {
@@ -95563,7 +95596,7 @@ const updateCommand$2 = make$4(
95563
95596
 
95564
95597
  const adminIntegrationCommandDescription = "Admin integration management (get, update, delete, tools, available)";
95565
95598
  const adminIntegrationSubcommands = [
95566
- getCommand$6,
95599
+ getCommand$7,
95567
95600
  updateCommand$2,
95568
95601
  deleteCommand$5,
95569
95602
  toolsCommand,
@@ -95873,14 +95906,14 @@ const deleteCommand$4 = make$4(
95873
95906
  const idArg$6 = text$2({ name: "id" }).pipe(
95874
95907
  withDescription$5("Preset ID")
95875
95908
  );
95876
- const jsonOption$c = boolean("json").pipe(
95909
+ const jsonOption$h = boolean("json").pipe(
95877
95910
  withDescription$3("Output in JSON format"),
95878
95911
  withDefault(false)
95879
95912
  );
95880
95913
  const formatPresetPrompt = maskSensitiveText;
95881
- const getCommand$5 = make$4(
95914
+ const getCommand$6 = make$4(
95882
95915
  "get",
95883
- { id: idArg$6, org: orgOption, json: jsonOption$c },
95916
+ { id: idArg$6, org: orgOption, json: jsonOption$h },
95884
95917
  ({ id, org, json }) => gen(function* () {
95885
95918
  const api = yield* ApiService;
95886
95919
  yield* resolveOrgSlug(org, api);
@@ -95927,7 +95960,7 @@ const getCommand$5 = make$4(
95927
95960
  })
95928
95961
  ).pipe(withDescription("Get preset details by ID"));
95929
95962
 
95930
- const jsonOption$b = boolean("json").pipe(
95963
+ const jsonOption$g = boolean("json").pipe(
95931
95964
  withDescription$3(
95932
95965
  'Output in JSON format: [{"id":"string","name":"string","isDefault":"boolean","prompt":"string|null","createdAt":"ISO-8601","updatedAt":"ISO-8601"}]'
95933
95966
  ),
@@ -95939,7 +95972,7 @@ function formatPresetListPrompt(prompt) {
95939
95972
  }
95940
95973
  const listCommand$9 = make$4(
95941
95974
  "list",
95942
- { org: orgOption, json: jsonOption$b },
95975
+ { org: orgOption, json: jsonOption$g },
95943
95976
  ({ org, json }) => gen(function* () {
95944
95977
  const api = yield* ApiService;
95945
95978
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -96165,7 +96198,7 @@ const updateCommand$1 = make$4(
96165
96198
  const presetCommandDescription = "Manage presets (list, get, create, update, rename, delete, set-default, unset-default, regen-key, regen-static-key)";
96166
96199
  const presetSubcommands = [
96167
96200
  listCommand$9,
96168
- getCommand$5,
96201
+ getCommand$6,
96169
96202
  createCommand$1,
96170
96203
  updateCommand$1,
96171
96204
  renameCommand$1,
@@ -96286,14 +96319,14 @@ const cloneBuiltInCommand = make$4(
96286
96319
  ).pipe(withDescription("Clone built-in skills from the library to your organization"));
96287
96320
 
96288
96321
  const skillIdArg$4 = text$2({ name: "id" }).pipe(withDescription$5("Skill ID to download"));
96289
- const outputOption$1 = text("output").pipe(
96322
+ const outputOption$2 = text("output").pipe(
96290
96323
  withAlias("o"),
96291
96324
  withDescription$3("Output file path (defaults to original filename)"),
96292
96325
  optional
96293
96326
  );
96294
96327
  const downloadCommand$1 = make$4(
96295
96328
  "download",
96296
- { skillId: skillIdArg$4, org: orgOption, output: outputOption$1 },
96329
+ { skillId: skillIdArg$4, org: orgOption, output: outputOption$2 },
96297
96330
  ({ skillId, org, output }) => gen(function* () {
96298
96331
  const api = yield* ApiService;
96299
96332
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -96372,7 +96405,7 @@ const applyOption = boolean("apply").pipe(
96372
96405
  withDescription$3("Apply the prepared GitHub update immediately"),
96373
96406
  withDefault(false)
96374
96407
  );
96375
- const jsonOption$a = boolean("json").pipe(
96408
+ const jsonOption$f = boolean("json").pipe(
96376
96409
  withDescription$3("Output in JSON format"),
96377
96410
  withDefault(false)
96378
96411
  );
@@ -96411,7 +96444,7 @@ const discardPreparedSkill = (api, orgSlug, preparedSkillId) => api.abortAdminUp
96411
96444
  );
96412
96445
  const inspectCommand = make$4(
96413
96446
  "inspect",
96414
- { githubUrl: githubUrlArg, org: orgOption, patEnv: patEnvOption, json: jsonOption$a },
96447
+ { githubUrl: githubUrlArg, org: orgOption, patEnv: patEnvOption, json: jsonOption$f },
96415
96448
  ({ githubUrl, org, patEnv, json }) => gen(function* () {
96416
96449
  const api = yield* ApiService;
96417
96450
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -96443,7 +96476,7 @@ const inspectCommand = make$4(
96443
96476
  ).pipe(withDescription("Inspect a GitHub repository for skill import"));
96444
96477
  const importCommand = make$4(
96445
96478
  "import",
96446
- { githubUrl: githubUrlArg, org: orgOption, patEnv: patEnvOption, overwrite: overwriteOption$2, json: jsonOption$a },
96479
+ { githubUrl: githubUrlArg, org: orgOption, patEnv: patEnvOption, overwrite: overwriteOption$2, json: jsonOption$f },
96447
96480
  ({ githubUrl, org, patEnv, overwrite, json }) => gen(function* () {
96448
96481
  const api = yield* ApiService;
96449
96482
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -96481,7 +96514,7 @@ const importCommand = make$4(
96481
96514
  ).pipe(withDescription("Import a skill from GitHub"));
96482
96515
  const syncCommand = make$4(
96483
96516
  "sync",
96484
- { skillId: skillIdArg$3, org: orgOption, apply: applyOption, json: jsonOption$a },
96517
+ { skillId: skillIdArg$3, org: orgOption, apply: applyOption, json: jsonOption$f },
96485
96518
  ({ skillId, org, apply, json }) => gen(function* () {
96486
96519
  const api = yield* ApiService;
96487
96520
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -96594,7 +96627,7 @@ const pageSizeOption = integer("page-size").pipe(
96594
96627
  withDescription$3("Page size (default: 20)"),
96595
96628
  optional
96596
96629
  );
96597
- const jsonOption$9 = boolean("json").pipe(
96630
+ const jsonOption$e = boolean("json").pipe(
96598
96631
  withDescription$3("Output in JSON format"),
96599
96632
  withDefault(false)
96600
96633
  );
@@ -96611,7 +96644,7 @@ const overwriteOption$1 = boolean("overwrite").pipe(
96611
96644
  );
96612
96645
  const listCommand$8 = make$4(
96613
96646
  "list",
96614
- { org: orgOption, search: searchOption$2, page: pageOption$2, pageSize: pageSizeOption, json: jsonOption$9 },
96647
+ { org: orgOption, search: searchOption$2, page: pageOption$2, pageSize: pageSizeOption, json: jsonOption$e },
96615
96648
  ({ org, search, page, pageSize, json }) => gen(function* () {
96616
96649
  const api = yield* ApiService;
96617
96650
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -96670,9 +96703,9 @@ const listCommand$8 = make$4(
96670
96703
  }
96671
96704
  })
96672
96705
  ).pipe(withDescription("List organization-wide skills"));
96673
- const getCommand$4 = make$4(
96706
+ const getCommand$5 = make$4(
96674
96707
  "get",
96675
- { skillId: skillIdArg$2, org: orgOption, json: jsonOption$9 },
96708
+ { skillId: skillIdArg$2, org: orgOption, json: jsonOption$e },
96676
96709
  ({ skillId, org, json }) => gen(function* () {
96677
96710
  const api = yield* ApiService;
96678
96711
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -96863,7 +96896,7 @@ const unpublishCommand = make$4(
96863
96896
  const adminSkillCommandDescription = "Admin skill management (list, get, upload, download, delete, publish, unpublish, built-in, clone-built-in, github)";
96864
96897
  const adminSkillSubcommands = [
96865
96898
  listCommand$8,
96866
- getCommand$4,
96899
+ getCommand$5,
96867
96900
  uploadCommand$2,
96868
96901
  downloadCommand$1,
96869
96902
  deleteCommand$3,
@@ -97283,7 +97316,7 @@ const agentIdOption = optional(
97283
97316
  withDescription$3("Agent installation ID (UUID) to chat with. Required when starting a new chat.")
97284
97317
  )
97285
97318
  );
97286
- const messageOption = optional(
97319
+ const messageOption$2 = optional(
97287
97320
  text("message").pipe(
97288
97321
  withAlias("m"),
97289
97322
  withDescription$3("Message to send to the agent (if not provided, reads from stdin when piped)")
@@ -97294,7 +97327,7 @@ const conversationIdOption = optional(
97294
97327
  withDescription$3("Continue an existing conversation by ID (from a previous --json result)")
97295
97328
  )
97296
97329
  );
97297
- const jsonOption$8 = boolean("json").pipe(
97330
+ const jsonOption$d = boolean("json").pipe(
97298
97331
  withDescription$3('Output in JSON format: {"text":"string","conversationId":"string"}'),
97299
97332
  withDefault(false)
97300
97333
  );
@@ -97308,7 +97341,7 @@ const readStdin = () => tryPromise({
97308
97341
  },
97309
97342
  catch: (e) => new ApiError({ message: `Failed to read stdin: ${e instanceof Error ? e.message : String(e)}` })
97310
97343
  });
97311
- const SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
97344
+ const SPINNER_FRAMES$1 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
97312
97345
  const SPINNER_MESSAGES = ["thinking", "churning", "processing", "pondering", "working", "computing"];
97313
97346
  const formatAgentChatTextOutput = maskSensitiveText;
97314
97347
  const startSpinner = () => {
@@ -97316,22 +97349,22 @@ const startSpinner = () => {
97316
97349
  let frameIdx = 0;
97317
97350
  let msgIdx = 0;
97318
97351
  const timer = setInterval(() => {
97319
- frameIdx = (frameIdx + 1) % SPINNER_FRAMES.length;
97352
+ frameIdx = (frameIdx + 1) % SPINNER_FRAMES$1.length;
97320
97353
  if (frameIdx === 0) msgIdx = (msgIdx + 1) % SPINNER_MESSAGES.length;
97321
- process.stderr.write(`\r${SPINNER_FRAMES[frameIdx]} ${SPINNER_MESSAGES[msgIdx]}... `);
97354
+ process.stderr.write(`\r${SPINNER_FRAMES$1[frameIdx]} ${SPINNER_MESSAGES[msgIdx]}... `);
97322
97355
  }, 80);
97323
97356
  return () => {
97324
97357
  clearInterval(timer);
97325
97358
  process.stderr.write("\r" + " ".repeat(30) + "\r");
97326
97359
  };
97327
97360
  };
97328
- const chatCommand$1 = make$4(
97361
+ const chatCommand$2 = make$4(
97329
97362
  "chat",
97330
97363
  {
97331
97364
  agentId: agentIdOption,
97332
- message: messageOption,
97365
+ message: messageOption$2,
97333
97366
  conversationId: conversationIdOption,
97334
- json: jsonOption$8,
97367
+ json: jsonOption$d,
97335
97368
  org: orgOption
97336
97369
  },
97337
97370
  ({ agentId, message, conversationId, json, org }) => gen(function* () {
@@ -97411,15 +97444,15 @@ const resolveAgentChatTarget = (agentId, conversationId) => {
97411
97444
  const isContinuationTarget = (target) => target.conversationId !== void 0;
97412
97445
 
97413
97446
  const agentIdArg$1 = text$2({ name: "id" }).pipe(withDescription$5("Agent ID"));
97414
- const jsonOption$7 = boolean("json").pipe(
97447
+ const jsonOption$c = boolean("json").pipe(
97415
97448
  withDescription$3(
97416
97449
  'Output in JSON format: {"id":"string","displayName":"string","shortDescription":"string","description":"string|null","createdBy":"string","createdAt":"ISO-8601","updatedAt":"ISO-8601","configRequiredIntegrations":["string"]}'
97417
97450
  ),
97418
97451
  withDefault(false)
97419
97452
  );
97420
- const getCommand$3 = make$4(
97453
+ const getCommand$4 = make$4(
97421
97454
  "get",
97422
- { agentId: agentIdArg$1, org: orgOption, json: jsonOption$7 },
97455
+ { agentId: agentIdArg$1, org: orgOption, json: jsonOption$c },
97423
97456
  ({ agentId, org, json }) => gen(function* () {
97424
97457
  const api = yield* ApiService;
97425
97458
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -97463,7 +97496,7 @@ const getCommand$3 = make$4(
97463
97496
  })
97464
97497
  ).pipe(withDescription("Get agent details by ID"));
97465
97498
 
97466
- const jsonOption$6 = boolean("json").pipe(
97499
+ const jsonOption$b = boolean("json").pipe(
97467
97500
  withDescription$3(
97468
97501
  'Output in JSON format: [{"id":"string","displayName":"string","agentType":"string","shortDescription":"string","createdBy":"string","createdAt":"ISO-8601"}]'
97469
97502
  ),
@@ -97471,7 +97504,7 @@ const jsonOption$6 = boolean("json").pipe(
97471
97504
  );
97472
97505
  const listCommand$7 = make$4(
97473
97506
  "list",
97474
- { org: orgOption, json: jsonOption$6 },
97507
+ { org: orgOption, json: jsonOption$b },
97475
97508
  ({ org, json }) => gen(function* () {
97476
97509
  const api = yield* ApiService;
97477
97510
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -97578,7 +97611,7 @@ const disableCommand$1 = make$4(
97578
97611
  ).pipe(withDescription("Disable an agent"));
97579
97612
 
97580
97613
  const agentCommandDescription = "Manage agents (list, get, chat, enable, disable)";
97581
- const agentSubcommands = [listCommand$7, getCommand$3, enableCommand$1, disableCommand$1, chatCommand$1];
97614
+ const agentSubcommands = [listCommand$7, getCommand$4, enableCommand$1, disableCommand$1, chatCommand$2];
97582
97615
  const agentCommand = make$4("agent", {}).pipe(
97583
97616
  withDescription(agentCommandDescription),
97584
97617
  withSubcommands(agentSubcommands)
@@ -98133,16 +98166,16 @@ const deleteCommand$2 = make$4(
98133
98166
  ).pipe(withDescription("Delete an automation by ID (requires --force to confirm)"));
98134
98167
 
98135
98168
  const automationIdArg$5 = text$2({ name: "id" }).pipe(withDescription$5("Automation ID"));
98136
- const jsonOption$5 = boolean("json").pipe(
98169
+ const jsonOption$a = boolean("json").pipe(
98137
98170
  withDescription$3(
98138
98171
  'Output in JSON format: {"id":"string","name":"string","status":"string","triggerType":"string","triggerProviderType":"string","agentId":"string","prompt":"string","lastExecutionTime":"ISO-8601|null","lastExecutionStatus":"string|null","lastExecutionChatThreadUrl":"string|null","nextExecutionTime":"ISO-8601|null","trigger":"object","createdAt":"ISO-8601","updatedAt":"ISO-8601"}'
98139
98172
  ),
98140
98173
  withDefault(false)
98141
98174
  );
98142
98175
  const formatAutomationTextOutput = maskSensitiveText;
98143
- const getCommand$2 = make$4(
98176
+ const getCommand$3 = make$4(
98144
98177
  "get",
98145
- { automationId: automationIdArg$5, org: orgOption, json: jsonOption$5 },
98178
+ { automationId: automationIdArg$5, org: orgOption, json: jsonOption$a },
98146
98179
  ({ automationId, org, json }) => gen(function* () {
98147
98180
  const api = yield* ApiService;
98148
98181
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -98253,7 +98286,7 @@ const nameOption = text("name").pipe(
98253
98286
  withDescription$3("Filter automations by name"),
98254
98287
  optional
98255
98288
  );
98256
- const jsonOption$4 = boolean("json").pipe(
98289
+ const jsonOption$9 = boolean("json").pipe(
98257
98290
  withDescription$3(
98258
98291
  'Output in JSON format: [{"id":"string","name":"string","status":"string","triggerType":"string","nextExecutionTime":"ISO-8601|null","createdAt":"ISO-8601"}]'
98259
98292
  ),
@@ -98261,7 +98294,7 @@ const jsonOption$4 = boolean("json").pipe(
98261
98294
  );
98262
98295
  const listCommand$6 = make$4(
98263
98296
  "list",
98264
- { org: orgOption, name: nameOption, json: jsonOption$4 },
98297
+ { org: orgOption, name: nameOption, json: jsonOption$9 },
98265
98298
  ({ org, name, json }) => gen(function* () {
98266
98299
  const api = yield* ApiService;
98267
98300
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -98564,7 +98597,7 @@ const webhookRegenCommand = make$4(
98564
98597
  const automationCommandDescription = "Manage automations (list, get, create, update, delete, run, enable, disable, history, cancel, webhook-regen, triggers)";
98565
98598
  const automationSubcommands = [
98566
98599
  listCommand$6,
98567
- getCommand$2,
98600
+ getCommand$3,
98568
98601
  createCommand,
98569
98602
  updateCommand,
98570
98603
  deleteCommand$2,
@@ -98584,7 +98617,7 @@ const automationCommand = make$4("automation", {}).pipe(
98584
98617
  const conversationIdArg$1 = text$2({ name: "conversationId" }).pipe(
98585
98618
  withDescription$5("Conversation ID")
98586
98619
  );
98587
- const getCommand$1 = make$4(
98620
+ const getCommand$2 = make$4(
98588
98621
  "get",
98589
98622
  { conversationId: conversationIdArg$1, org: orgOption },
98590
98623
  ({ conversationId, org }) => gen(function* () {
@@ -98623,7 +98656,7 @@ const getCommand$1 = make$4(
98623
98656
  })
98624
98657
  ).pipe(withDescription("Get conversation details by ID"));
98625
98658
 
98626
- const jsonOption$3 = boolean("json").pipe(
98659
+ const jsonOption$8 = boolean("json").pipe(
98627
98660
  withDescription$3(
98628
98661
  'Output in JSON format: [{"conversationId":"string","title":"string","type":"string","updatedAt":"ISO-8601"}]'
98629
98662
  ),
@@ -98631,7 +98664,7 @@ const jsonOption$3 = boolean("json").pipe(
98631
98664
  );
98632
98665
  const listCommand$5 = make$4(
98633
98666
  "list",
98634
- { org: orgOption, json: jsonOption$3 },
98667
+ { org: orgOption, json: jsonOption$8 },
98635
98668
  ({ org, json }) => gen(function* () {
98636
98669
  const api = yield* ApiService;
98637
98670
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -98723,9 +98756,174 @@ const messagesCommand = make$4(
98723
98756
  })
98724
98757
  ).pipe(withDescription("List messages in a conversation"));
98725
98758
 
98726
- const chatCommandDescription = "Manage conversations (list, get, messages)";
98727
- const chatSubcommands = [listCommand$5, getCommand$1, messagesCommand];
98728
- const chatCommand = make$4("chat", {}).pipe(
98759
+ const filterModelExecModels = (catalog, type) => {
98760
+ if (!type) {
98761
+ return catalog.models;
98762
+ }
98763
+ return catalog.models.filter((model) => model.type === type);
98764
+ };
98765
+ const getModelExecInputSchema = (model) => {
98766
+ if (model.inputSchema) {
98767
+ return model.inputSchema;
98768
+ }
98769
+ if (model.type === "image-generation") {
98770
+ return {
98771
+ type: "object",
98772
+ required: ["prompt"],
98773
+ properties: {
98774
+ prompt: { type: "string" },
98775
+ options: {
98776
+ type: "object",
98777
+ properties: {
98778
+ size: { type: "string" },
98779
+ n: { type: "integer" }
98780
+ }
98781
+ }
98782
+ }
98783
+ };
98784
+ }
98785
+ return {
98786
+ type: "object",
98787
+ required: ["input"],
98788
+ properties: {
98789
+ input: {
98790
+ type: "object",
98791
+ required: ["text"],
98792
+ properties: {
98793
+ text: { type: "string" }
98794
+ }
98795
+ },
98796
+ options: { type: "object" }
98797
+ }
98798
+ };
98799
+ };
98800
+ const buildModelExecSampleInput = (model) => {
98801
+ if (model.type === "image-generation") {
98802
+ return {
98803
+ prompt: "A small robot reading a book",
98804
+ options: { size: "1024x1024", n: 1 }
98805
+ };
98806
+ }
98807
+ return {
98808
+ input: { text: "Say hello in one sentence." },
98809
+ options: {}
98810
+ };
98811
+ };
98812
+ const buildModelExecChatRequest = (model, input) => ({
98813
+ model,
98814
+ input
98815
+ });
98816
+ const buildModelExecImageGenerationRequest = (model, input) => ({
98817
+ model,
98818
+ prompt: input.prompt,
98819
+ ...input.options ? { options: input.options } : {}
98820
+ });
98821
+ const readJsonInputFile = (path) => tryPromise({
98822
+ try: async () => JSON.parse(await fs$1.readFile(path, "utf8")),
98823
+ catch: (error) => new ApiError({
98824
+ message: `Failed to read JSON input "${path}": ${error instanceof Error ? error.message : String(error)}`
98825
+ })
98826
+ });
98827
+ const saveJsonFile = (path, value) => tryPromise({
98828
+ try: async () => {
98829
+ await fs$1.writeFile(path, `${JSON.stringify(value, null, 2)}
98830
+ `, "utf8");
98831
+ },
98832
+ catch: (error) => new ApiError({
98833
+ message: `Failed to write "${path}": ${error instanceof Error ? error.message : String(error)}`
98834
+ })
98835
+ });
98836
+ const readStdinText = () => tryPromise({
98837
+ try: async () => {
98838
+ const chunks = [];
98839
+ for await (const chunk of process.stdin) {
98840
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
98841
+ }
98842
+ return Buffer.concat(chunks).toString("utf8").trim();
98843
+ },
98844
+ catch: (error) => new ApiError({
98845
+ message: `Failed to read stdin: ${error instanceof Error ? error.message : String(error)}`
98846
+ })
98847
+ });
98848
+
98849
+ const messageOption$1 = optional(
98850
+ text("message").pipe(
98851
+ withAlias("m"),
98852
+ withDescription$3("Message text. If omitted, stdin is used when piped.")
98853
+ )
98854
+ );
98855
+ const conversationOption = optional(
98856
+ text("conversation").pipe(
98857
+ withDescription$3("Conversation ID to continue")
98858
+ )
98859
+ );
98860
+ const modelOption$2 = optional(
98861
+ text("model").pipe(withDescription$3("Chat model ID for a new or continued conversation"))
98862
+ );
98863
+ const jsonOption$7 = boolean("json").pipe(
98864
+ withDescription$3("Output full response as JSON"),
98865
+ withDefault(false)
98866
+ );
98867
+ const buildSendChatMessageRequest = (input) => ({
98868
+ conversationId: getOrUndefined(input.conversationId),
98869
+ model: getOrUndefined(input.model),
98870
+ message: { text: input.text }
98871
+ });
98872
+ const sendCommand = make$4(
98873
+ "send",
98874
+ {
98875
+ message: messageOption$1,
98876
+ conversation: conversationOption,
98877
+ model: modelOption$2,
98878
+ json: jsonOption$7,
98879
+ org: orgOption
98880
+ },
98881
+ ({ message, conversation, model, json, org }) => gen(function* () {
98882
+ const api = yield* ApiService;
98883
+ const orgSlug = yield* resolveOrgSlug(org, api);
98884
+ const text = yield* resolveMessageText(message);
98885
+ const request = buildSendChatMessageRequest({ conversationId: conversation, model, text });
98886
+ const result = yield* api.sendChatMessage(orgSlug, request).pipe(
98887
+ catchTag(
98888
+ "NotLoggedInError",
98889
+ (error) => gen(function* () {
98890
+ yield* error$1(error.message);
98891
+ return yield* fail$3(error);
98892
+ })
98893
+ ),
98894
+ catchTag(
98895
+ "ApiError",
98896
+ (error) => gen(function* () {
98897
+ yield* error$1(`Chat send failed: ${error.message}`);
98898
+ return yield* fail$3(error);
98899
+ })
98900
+ )
98901
+ );
98902
+ if (json) {
98903
+ yield* log(jsonFormat(result));
98904
+ return;
98905
+ }
98906
+ yield* log(maskSensitiveText(result.output.text));
98907
+ yield* log(`
98908
+ Conversation: ${result.conversationId}`);
98909
+ })
98910
+ ).pipe(withDescription("Send a conversation-based chat message and print the assistant response"));
98911
+ const resolveMessageText = (message) => gen(function* () {
98912
+ if (isSome(message)) {
98913
+ return message.value;
98914
+ }
98915
+ if (!process.stdin.isTTY) {
98916
+ const text = yield* readStdinText();
98917
+ if (text) {
98918
+ return text;
98919
+ }
98920
+ }
98921
+ return yield* fail$3(new ApiError({ message: "Provide --message/-m or pipe text through stdin." }));
98922
+ });
98923
+
98924
+ const chatCommandDescription = "Manage conversations (list, get, messages, send)";
98925
+ const chatSubcommands = [listCommand$5, getCommand$2, messagesCommand, sendCommand];
98926
+ const chatCommand$1 = make$4("chat", {}).pipe(
98729
98927
  withDescription(chatCommandDescription),
98730
98928
  withSubcommands(chatSubcommands)
98731
98929
  );
@@ -98882,13 +99080,13 @@ const deleteCommand$1 = make$4(
98882
99080
  ).pipe(withDescription("Delete drive entries by ID (requires --force to confirm)"));
98883
99081
 
98884
99082
  const entryIdArg$1 = text$2({ name: "id" }).pipe(withDescription$5("Drive entry ID"));
98885
- const outputOption = text("output").pipe(
99083
+ const outputOption$1 = text("output").pipe(
98886
99084
  withAlias("o"),
98887
99085
  withDescription$3("Output file path (required, as the API does not return the original filename)")
98888
99086
  );
98889
99087
  const downloadCommand = make$4(
98890
99088
  "download",
98891
- { entryId: entryIdArg$1, org: orgOption, output: outputOption },
99089
+ { entryId: entryIdArg$1, org: orgOption, output: outputOption$1 },
98892
99090
  ({ entryId, org, output }) => gen(function* () {
98893
99091
  const api = yield* ApiService;
98894
99092
  const orgSlug = yield* resolveOrgSlug(org, api);
@@ -99309,6 +99507,347 @@ const modelCommand = make$4("model", {}).pipe(
99309
99507
  withSubcommands(modelSubcommands)
99310
99508
  );
99311
99509
 
99510
+ const SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
99511
+ const SPINNER_INTERVAL_MS = 80;
99512
+ const CLEAR_WIDTH = 80;
99513
+ const startModelExecProgress = (message, options = {}) => {
99514
+ const stream = options.stream ?? process.stderr;
99515
+ if (!stream.isTTY) return () => void 0;
99516
+ const now = options.now ?? Date.now;
99517
+ const startedAt = now();
99518
+ let frameIndex = 0;
99519
+ let stopped = false;
99520
+ const render = () => {
99521
+ const elapsedSeconds = Math.floor((now() - startedAt) / 1e3);
99522
+ stream.write(`\r${SPINNER_FRAMES[frameIndex]} ${message}... ${elapsedSeconds}s`);
99523
+ frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
99524
+ };
99525
+ render();
99526
+ const timer = setInterval(render, SPINNER_INTERVAL_MS);
99527
+ return () => {
99528
+ if (stopped) return;
99529
+ stopped = true;
99530
+ clearInterval(timer);
99531
+ stream.write(`\r${" ".repeat(CLEAR_WIDTH)}\r`);
99532
+ };
99533
+ };
99534
+
99535
+ const modelOption$1 = text("model").pipe(withDescription$3("Chat model ID"));
99536
+ const messageOption = optional(
99537
+ text("message").pipe(
99538
+ withAlias("m"),
99539
+ withDescription$3("Message text. If omitted, stdin is used when piped.")
99540
+ )
99541
+ );
99542
+ const inputOption$1 = optional(
99543
+ text("input").pipe(withDescription$3("JSON input file generated by model-exec sample"))
99544
+ );
99545
+ const jsonOption$6 = boolean("json").pipe(
99546
+ withDescription$3("Output full response as JSON"),
99547
+ withDefault(false)
99548
+ );
99549
+ const chatCommand = make$4(
99550
+ "chat",
99551
+ { model: modelOption$1, message: messageOption, input: inputOption$1, json: jsonOption$6, org: orgOption },
99552
+ ({ model, message, input, json, org }) => gen(function* () {
99553
+ const api = yield* ApiService;
99554
+ const orgSlug = yield* resolveOrgSlug(org, api);
99555
+ const request = yield* resolveChatRequest(model, message, input);
99556
+ const stopProgress = startModelExecProgress("Waiting for model response");
99557
+ const result = yield* api.executeModelExecChat(orgSlug, request).pipe(
99558
+ ensuring$1(sync$1(stopProgress)),
99559
+ catchTag(
99560
+ "NotLoggedInError",
99561
+ (error) => gen(function* () {
99562
+ yield* error$1(error.message);
99563
+ return yield* fail$3(error);
99564
+ })
99565
+ ),
99566
+ catchTag(
99567
+ "ApiError",
99568
+ (error) => gen(function* () {
99569
+ yield* error$1(`Model-exec chat failed: ${error.message}`);
99570
+ return yield* fail$3(error);
99571
+ })
99572
+ )
99573
+ );
99574
+ if (json) {
99575
+ yield* log(jsonFormat(result));
99576
+ return;
99577
+ }
99578
+ yield* log(maskSensitiveText(result.output.text));
99579
+ })
99580
+ ).pipe(withDescription("Execute a chat model once without creating a conversation"));
99581
+ const resolveChatRequest = (model, message, input) => gen(function* () {
99582
+ if (isSome(input)) {
99583
+ const parsed = yield* readJsonInputFile(input.value);
99584
+ const text = parsed.input?.text;
99585
+ if (!text) {
99586
+ return yield* fail$3(new ApiError({ message: "Chat input JSON must include input.text" }));
99587
+ }
99588
+ return {
99589
+ model,
99590
+ input: { ...parsed.input, text },
99591
+ ...parsed.options ? { options: parsed.options } : {}
99592
+ };
99593
+ }
99594
+ if (isSome(message)) {
99595
+ return buildModelExecChatRequest(model, { text: message.value });
99596
+ }
99597
+ if (!process.stdin.isTTY) {
99598
+ const text = yield* readStdinText();
99599
+ if (text) {
99600
+ return buildModelExecChatRequest(model, { text });
99601
+ }
99602
+ }
99603
+ return yield* fail$3(new ApiError({ message: "Provide --message/-m, --input, or pipe text through stdin." }));
99604
+ });
99605
+
99606
+ const modelIdArg$1 = text$2({ name: "model-id" }).pipe(withDescription$5("Model ID"));
99607
+ const jsonOption$5 = boolean("json").pipe(
99608
+ withDescription$3("Output model detail as JSON"),
99609
+ withDefault(false)
99610
+ );
99611
+ const getCommand$1 = make$4(
99612
+ "get",
99613
+ { modelId: modelIdArg$1, org: orgOption, json: jsonOption$5 },
99614
+ ({ modelId, org, json }) => gen(function* () {
99615
+ const api = yield* ApiService;
99616
+ const orgSlug = yield* resolveOrgSlug(org, api);
99617
+ const catalog = yield* api.listModelExecModels(orgSlug).pipe(
99618
+ catchTag(
99619
+ "NotLoggedInError",
99620
+ (error) => gen(function* () {
99621
+ yield* error$1(error.message);
99622
+ return yield* fail$3(error);
99623
+ })
99624
+ ),
99625
+ catchTag(
99626
+ "ApiError",
99627
+ (error) => gen(function* () {
99628
+ yield* error$1(`Failed to fetch model "${modelId}": ${error.message}`);
99629
+ return yield* fail$3(error);
99630
+ })
99631
+ )
99632
+ );
99633
+ const model = catalog.models.find((item) => item.id === modelId);
99634
+ if (!model) {
99635
+ const error = new ApiError({ message: `Model "${modelId}" not found.`, statusCode: 404 });
99636
+ yield* error$1(error.message);
99637
+ return yield* fail$3(error);
99638
+ }
99639
+ const detail = { ...model, inputSchema: getModelExecInputSchema(model) };
99640
+ if (json) {
99641
+ yield* log(jsonFormat(detail));
99642
+ return;
99643
+ }
99644
+ yield* log(`Model: ${detail.id}`);
99645
+ yield* log(` Type: ${detail.type}`);
99646
+ if (detail.displayName) yield* log(` Name: ${detail.displayName}`);
99647
+ yield* log(" Input schema:");
99648
+ yield* log(jsonFormat(detail.inputSchema));
99649
+ })
99650
+ ).pipe(withDescription("Show model-exec model detail and input schema"));
99651
+
99652
+ const modelOption = text("model").pipe(withDescription$3("Image-generation model ID"));
99653
+ const promptOption = optional(
99654
+ text("prompt").pipe(withDescription$3("Image prompt"))
99655
+ );
99656
+ const inputOption = optional(
99657
+ text("input").pipe(withDescription$3("JSON input file generated by model-exec sample"))
99658
+ );
99659
+ const outputOption = text("output").pipe(withDescription$3("Output image file path"));
99660
+ const jsonOption$4 = boolean("json").pipe(
99661
+ withDescription$3("Output full response as JSON after saving the image"),
99662
+ withDefault(false)
99663
+ );
99664
+ const generateCommand = make$4(
99665
+ "generate",
99666
+ {
99667
+ model: modelOption,
99668
+ prompt: promptOption,
99669
+ input: inputOption,
99670
+ output: outputOption,
99671
+ json: jsonOption$4,
99672
+ org: orgOption
99673
+ },
99674
+ ({ model, prompt, input, output, json, org }) => gen(function* () {
99675
+ const api = yield* ApiService;
99676
+ const orgSlug = yield* resolveOrgSlug(org, api);
99677
+ const request = yield* resolveImageRequest(model, prompt, input);
99678
+ const stopProgress = startModelExecProgress("Generating image (timeout: 5m)");
99679
+ const result = yield* gen(function* () {
99680
+ const response = yield* api.executeModelExecImageGeneration(orgSlug, request);
99681
+ const first = response.outputs[0];
99682
+ if (!first) {
99683
+ return yield* fail$3(new ApiError({ message: "Image generation returned no outputs." }));
99684
+ }
99685
+ const buffer = yield* api.downloadFromUrl(first.downloadUrl);
99686
+ yield* writeImageFile(output, buffer);
99687
+ return response;
99688
+ }).pipe(
99689
+ ensuring$1(sync$1(stopProgress)),
99690
+ catchTag(
99691
+ "NotLoggedInError",
99692
+ (error) => gen(function* () {
99693
+ yield* error$1(error.message);
99694
+ return yield* fail$3(error);
99695
+ })
99696
+ ),
99697
+ catchTag(
99698
+ "ApiError",
99699
+ (error) => gen(function* () {
99700
+ yield* error$1(`Image generation failed: ${error.message}`);
99701
+ return yield* fail$3(error);
99702
+ })
99703
+ )
99704
+ );
99705
+ if (json) {
99706
+ yield* log(jsonFormat({ ...result, savedPath: output }));
99707
+ return;
99708
+ }
99709
+ yield* log(`Saved image to ${output}`);
99710
+ })
99711
+ ).pipe(
99712
+ withDescription(
99713
+ "Generate an image once and save the first output artifact (5-minute timeout)"
99714
+ )
99715
+ );
99716
+ const imageCommand = make$4("image", {}).pipe(
99717
+ withDescription("Run image-generation model-exec commands"),
99718
+ withSubcommands([generateCommand])
99719
+ );
99720
+ const resolveImageRequest = (model, prompt, input) => gen(function* () {
99721
+ if (isSome(input)) {
99722
+ const parsed = yield* readJsonInputFile(input.value);
99723
+ if (!parsed.prompt) {
99724
+ return yield* fail$3(new ApiError({ message: "Image input JSON must include prompt" }));
99725
+ }
99726
+ return buildModelExecImageGenerationRequest(model, {
99727
+ prompt: parsed.prompt,
99728
+ ...parsed.options ? { options: parsed.options } : {}
99729
+ });
99730
+ }
99731
+ if (isSome(prompt)) {
99732
+ return buildModelExecImageGenerationRequest(model, { prompt: prompt.value });
99733
+ }
99734
+ return yield* fail$3(new ApiError({ message: "Provide --prompt or --input." }));
99735
+ });
99736
+ const writeImageFile = (path, buffer) => tryPromise({
99737
+ try: async () => {
99738
+ await fs$1.writeFile(path, buffer);
99739
+ },
99740
+ catch: (error) => new ApiError({
99741
+ message: `Failed to write image "${path}": ${error instanceof Error ? error.message : String(error)}`
99742
+ })
99743
+ });
99744
+
99745
+ const typeOption = optional(
99746
+ text("type").pipe(
99747
+ withDescription$3("Filter by model type: chat, image-generation, embedding")
99748
+ )
99749
+ );
99750
+ const jsonOption$3 = boolean("json").pipe(
99751
+ withDescription$3("Output model catalog as JSON"),
99752
+ withDefault(false)
99753
+ );
99754
+ const modelsCommand = make$4(
99755
+ "models",
99756
+ { org: orgOption, type: typeOption, json: jsonOption$3 },
99757
+ ({ org, type, json }) => gen(function* () {
99758
+ const api = yield* ApiService;
99759
+ const orgSlug = yield* resolveOrgSlug(org, api);
99760
+ const catalog = yield* api.listModelExecModels(orgSlug).pipe(
99761
+ catchTag(
99762
+ "NotLoggedInError",
99763
+ (error) => gen(function* () {
99764
+ yield* error$1(error.message);
99765
+ return yield* fail$3(error);
99766
+ })
99767
+ ),
99768
+ catchTag(
99769
+ "ApiError",
99770
+ (error) => gen(function* () {
99771
+ yield* error$1(`Failed to fetch model-exec models: ${error.message}`);
99772
+ return yield* fail$3(error);
99773
+ })
99774
+ )
99775
+ );
99776
+ const models = filterModelExecModels(catalog, getOrUndefined(type));
99777
+ if (json) {
99778
+ yield* log(jsonFormat({ ...catalog, models }));
99779
+ return;
99780
+ }
99781
+ if (models.length === 0) {
99782
+ yield* log("No model-exec models available.");
99783
+ return;
99784
+ }
99785
+ yield* log(`Model-exec models in organization "${orgSlug}":
99786
+ `);
99787
+ for (const model of models) {
99788
+ const name = model.displayName ? ` (${model.displayName})` : "";
99789
+ const marker = model.default ? " default" : "";
99790
+ yield* log(` ${model.id}${name}`);
99791
+ yield* log(` Type: ${model.type}${marker}`);
99792
+ }
99793
+ })
99794
+ ).pipe(withDescription("List experimental model-exec models by capability"));
99795
+
99796
+ const modelIdArg = text$2({ name: "model-id" }).pipe(withDescription$5("Model ID"));
99797
+ const saveOption = optional(
99798
+ text("save").pipe(withDescription$3("Save sample JSON to a file"))
99799
+ );
99800
+ const sampleCommand = make$4(
99801
+ "sample",
99802
+ { modelId: modelIdArg, org: orgOption, save: saveOption },
99803
+ ({ modelId, org, save }) => gen(function* () {
99804
+ const api = yield* ApiService;
99805
+ const orgSlug = yield* resolveOrgSlug(org, api);
99806
+ const catalog = yield* api.listModelExecModels(orgSlug).pipe(
99807
+ catchTag(
99808
+ "NotLoggedInError",
99809
+ (error) => gen(function* () {
99810
+ yield* error$1(error.message);
99811
+ return yield* fail$3(error);
99812
+ })
99813
+ ),
99814
+ catchTag(
99815
+ "ApiError",
99816
+ (error) => gen(function* () {
99817
+ yield* error$1(`Failed to fetch model "${modelId}": ${error.message}`);
99818
+ return yield* fail$3(error);
99819
+ })
99820
+ )
99821
+ );
99822
+ const model = catalog.models.find((item) => item.id === modelId);
99823
+ if (!model) {
99824
+ const error = new ApiError({ message: `Model "${modelId}" not found.`, statusCode: 404 });
99825
+ yield* error$1(error.message);
99826
+ return yield* fail$3(error);
99827
+ }
99828
+ const sample = buildModelExecSampleInput(model);
99829
+ if (isSome(save)) {
99830
+ yield* saveJsonFile(save.value, sample);
99831
+ yield* log(`Saved sample input to ${save.value}`);
99832
+ return;
99833
+ }
99834
+ yield* log(jsonFormat(sample));
99835
+ })
99836
+ ).pipe(withDescription("Generate sample JSON input for a model-exec model"));
99837
+
99838
+ const modelExecCommandDescription = "Experimental one-shot model execution for chat and image-generation models";
99839
+ const modelExecSubcommands = [
99840
+ modelsCommand,
99841
+ getCommand$1,
99842
+ sampleCommand,
99843
+ chatCommand,
99844
+ imageCommand
99845
+ ];
99846
+ const modelExecCommand = make$4("model-exec", {}).pipe(
99847
+ withDescription(modelExecCommandDescription),
99848
+ withSubcommands(modelExecSubcommands)
99849
+ );
99850
+
99312
99851
  const capabilitiesCommand = make$4(
99313
99852
  "capabilities",
99314
99853
  { org: orgOption },
@@ -100001,11 +100540,12 @@ const rootSubcommands = [
100001
100540
  agentCommand,
100002
100541
  authCommand,
100003
100542
  automationCommand,
100004
- chatCommand,
100543
+ chatCommand$1,
100005
100544
  configCommand,
100006
100545
  driveCommand,
100007
100546
  integrationCommand,
100008
100547
  modelCommand,
100548
+ modelExecCommand,
100009
100549
  orgCommand,
100010
100550
  skillCommand,
100011
100551
  ...nativeSubcommands
@@ -58,6 +58,7 @@
58
58
  | P0-DRIVE-02 | drive | `aipctl drive upload` | `createDriveUploadUrl` → `uploadToS3` → `completeDriveUpload` | api + external | `POST` → `PUT` → `POST` | `/drive/entries/upload-url`; external presigned `uploadUrl`; `/drive/entries/{entryId}/complete` | official upload request `{ parentId: null, filename, fileSize, mimeType }` | official `CreateDriveUploadUrlResponse { id, filename, uploadUrl, headers }` then `void` | `resource/oas/modules/backend/organization/storage/openapi.yaml` | `frontend/packages/api/src/out/modules/organization-storage-types.ts#paths["/api/web/organizations/slug/{organizationSlug}/drive/entries/upload-url"]`, `#paths["/api/web/organizations/slug/{organizationSlug}/drive/entries/{entryId}/complete"]` | `generated-ready` | `P0` | `migrate` | none | `AIP-PUB-05` | updated `src/core/api.test.ts`, `commands/drive/drive.test.ts`; covers `parentId: null` request and response `id`/`filename` usage |
59
59
  | P1-INT-01 | integration | `aipctl integration list`, `test`; `aipctl admin integration get`, `update`, `delete`, `tools`, `available` | `listIntegrations`, `testIntegration`, `getIntegration`, `updateIntegration`, `deleteIntegration`, `listIntegrationTools`, `listConfigurableIntegrations` | api | `GET`, `POST`, `GET`, `PUT`, `DELETE`, `GET`, `GET` | `/api/web/integrations/v2`; `/api/web/integration/{integrationId}/test`; `/api/web/admin/organizations/slug/{organizationSlug}/integrations/{integrationId}`; `/{integrationId}/tools`; `/configurable-integrations` | top-level list/test and admin detail/available now follow current/legacy OAS path+body+DTO where available; admin update/delete still accept untyped legacy mutation body/path until admin mutation contract lands | local `Integration`, `IntegrationDetail`, `IntegrationToolsResponse`, `ConfigurableIntegration[]` | legacy admin paths: `resource/oas/duplo-web.yaml`; partial modern source: `resource/oas/modules/backend/integration/openapi.yaml` (does not cover these admin list/CRUD/tools paths) | legacy `frontend/packages/api/src/out/user-types.ts#paths["/api/web/admin/organizations/slug/{organizationSlug}/integrations"]`, `#paths["/api/web/admin/organizations/slug/{organizationSlug}/configurable-integrations"]`; partial current `frontend/packages/api/src/out/modules/integration-types.ts` lacks these admin list/CRUD/tools paths | `legacy-generated` | `P1` | `legacy-exception` | owner=`backend app Integrations / duplo-web legacy OAS`; rationale=`public beta keeps integration list/test/admin detail/tools/available, but admin update/delete still lack an approved current contract outside legacy duplo-web`; removal condition=`independent contract covers user list/test plus admin CRUD/tools/available`; incorporation plan=`extend integration module or independent contract for list/test/admin CRUD/tools/available, then retire legacy path/body mappings and delete/update exception` | N/A | updated `src/core/api.test.ts`, `commands/integration/integration.test.ts`; covers v2 list query, test body `{ organizationSlug }`, admin detail/tools DTO fields, configurable wrapper unwrap, and pinned update/delete legacy paths |
60
60
  | P0-MODEL-01 | model | `aipctl model list` | `listModels` | chat | `GET` | `/api/{orgSlug}/models` | none | CLI maps official `{ models: string[], defaultModel? }` into `Model[]` with optional `isDefault` | `resource/oas/chat.yaml` | `frontend/packages/api/src/out/chat-types.ts#paths["/api/{orgSlug}/models"]` | `legacy-generated` | `P0` | `migrate` | none | `AIP-PUB-05` | updated `src/core/api.test.ts`, `commands/model/model.test.ts`; covers official path and string-array response mapping |
61
+ | P1-MODEL-EXEC-01 | model-exec | `aipctl model-exec models`, `get`, `sample`, `chat`, `image generate` | `listModelExecModels`, `executeModelExecChat`, `executeModelExecImageGeneration`; image output uses `downloadFromUrl` | chat + external artifact URL | `GET`, `POST`, `POST`, external `GET` | `/api/{orgSlug}/cli/model-exec/models`; `/chat`; `/images/generations`; response `downloadUrl` | local `ModelExecChatRequest`, `ModelExecImageGenerationRequest`; Chat DTO source `chat/packages/chat/src/modules/cli-model-exec/interface/dto/cli-model-exec.dto.ts` | local `ModelExecModelCatalog`, `ModelExecChatResponse`, `ModelExecImageGenerationResponse`; image bytes from external URL | not found in current `resource/oas`; implemented by `chat/packages/chat/src/modules/cli-model-exec/interface/cli-model-exec.interface.controller.ts` | none | `oas-missing` | `P1` | `legacy-exception` | owner=`chat CLI Model Exec API`; rationale=`experimental public command is implemented with Chat zod DTOs but no current OAS path`; removal condition=`CLI model-exec paths and DTOs are published in the Chat contract`; incorporation plan=`add the three paths and schemas to the Chat OAS, generate an independent reference, then bind CLI DTO/tests` | `AIP-PUB-05` | existing `src/core/api.test.ts`, `commands/model-exec/model-exec.test.ts`, `commands/model-exec/progress.test.ts`; covers three paths, request bodies, required Chat DTO response fields, helper/progress behavior. Needed: image command response → external download → file write regression test |
61
62
  | USAGE-01 | usage | `aipctl admin usage credit`, `top-users`, `daily-graph`, `daily-users`, `mcp-top` | `getCreditStatistics`, `getCreditTopUsers`, `getCreditDailyGraph`, `getCreditDailyUsers`, `getMcpTopTools` | api | `GET` | `/api/web/admin/organizations/slug/{organizationSlug}/usage/...` | query `{ startDt,endDt,limit,pageNumber,pageSize,userId }` | local usage DTOs, `DailyUsageDetailsResponse.pagination { page,pageSize,total }` | `resource/oas/modules/backend/usage/openapi.yaml` | `frontend/packages/api/src/out/modules/usage-types.ts#paths[...]` | `generated-ready` | N/A | `keep` | none | N/A | existing `commands/usage/usage.test.ts`; needed generated path snapshots |
62
63
  | AUDIT-01 | audit | `aipctl admin audit list`, `types` | `listAuditLogs`, `listAuditEventTypes` | api | `GET` | `/api/web/audit/organization/slug/{organizationSlug}`; `/api/web/audit/event/types` | query `{ eventType,page,pageSize }` | local `ListAuditResponse`, `AuditEventType[]` | `resource/oas/modules/backend/audit/openapi.yaml` | `frontend/packages/api/src/out/modules/backend-audit-types.ts#paths[...]` | `generated-ready` | N/A | `keep` | none | N/A | existing `commands/audit/audit.test.ts`; needed generated path snapshots |
63
64
  | TUNNEL-01 | tunnel | `aipctl admin tunnel status`, `config`, `token` | `getTunnelStatus`, `getTunnelConfig`, `createTunnelToken` | api | `GET`, `GET`, `POST` | `/api/web/edge-tunnel/v2/organizations/slug/{organizationSlug}/status`; `/config`; `/token` | none | local `TunnelStatus`, `TunnelConfig`, `void` | `resource/oas/modules/backend/tunnel/openapi.yaml` | `frontend/packages/api/src/out/modules/tunnel-types.ts#paths[...]` | `generated-ready` | N/A | `keep` | none | N/A | existing `commands/tunnel/tunnel.test.ts`; needed v2 path snapshot |
@@ -82,6 +83,7 @@
82
83
  - `P1-PRESET-01` preset admin namespace/docs + legacy path tests done
83
84
  - `P1-SKILL-01` skill JSON/help/docs field sync done
84
85
  - `P1-CHAT-01` chat SSE fixture tests added for final/fallback/error cases
86
+ - `P1-MODEL-EXEC-01` experimental CLI model-exec contract remains OAS-missing; current Chat controller/DTO and CLI path tests are recorded
85
87
 
86
88
  ## Manual notes
87
89
 
@@ -59,7 +59,7 @@ desktop/packages/cli/
59
59
 
60
60
  - package base version: `0.0.0`
61
61
  - publish-time CalVer가 `AIPCTL_VERSION`으로 TS bundle과 Go ldflags에 주입됩니다.
62
- - `AIPCTL_BUILD_ENV`는 `local|dev|stage|prod|test` 중 하나이며 `logs` gating에 사용됩니다.
62
+ - `AIPCTL_BUILD_ENV`는 `local|dev|qa|stage|prod|test` 중 하나이며 `logs` gating에 사용됩니다.
63
63
  - native input hash가 이전 발행본과 같으면 platform package를 새로 빌드하지 않고 기존 version을 재사용합니다.
64
64
 
65
65
  ## Dependency direction
@@ -4,7 +4,7 @@
4
4
 
5
5
  공통 규칙은 중앙 문서를 따른다.
6
6
 
7
- - [docs/rules/go-coding-guidelines.md](/Users/evan/Desktop/Work/duplo/docs/rules/go-coding-guidelines.md)
7
+ - [docs/rules/go-coding-guidelines.md](../../../../docs/rules/go-coding-guidelines.md)
8
8
 
9
9
  ## `desktop`에서 특히 중요한 규칙
10
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aipctl",
3
- "version": "2026.8.5",
3
+ "version": "2026.9.1",
4
4
  "description": "QueryPie AIP and MCP CLI tool",
5
5
  "author": "QueryPie",
6
6
  "license": "UNLICENSED",
@@ -22,16 +22,16 @@
22
22
  "node": ">=24.18.0"
23
23
  },
24
24
  "metadata": {
25
- "contentHash": "156db2615eb6ffb678be96f2e25f38d9628decf8121bcbf638755924ef6fe6ca",
26
- "wrapperHash": "bf02d1a5ff6b11af9ea359c0e5996a361d47b0f9f5414b7e590477deae7cfe9f",
27
- "nativeHash": "bfaa115ac53b30ba141fbe842585a0ed0fca487e2eb356cda219516e53c8b4c0"
25
+ "contentHash": "436898e8024a53e6caa75dbc99198a467cea9200459511eebcee6e3ca4b39995",
26
+ "wrapperHash": "a1c87902f0caa915d2dbfd79729e0c4dba47acabaee56f6e37ea6f341b8ac505",
27
+ "nativeHash": "006b08c3e391f4571d9b7c0f12d684fe2448106259e7e0581b10f9be362452e9"
28
28
  },
29
29
  "optionalDependencies": {
30
- "aipctl-darwin-x64": "2026.8.5",
31
- "aipctl-darwin-arm64": "2026.8.5",
32
- "aipctl-linux-x64": "2026.8.5",
33
- "aipctl-linux-arm64": "2026.8.5",
34
- "aipctl-windows-x64": "2026.8.5",
35
- "aipctl-windows-arm64": "2026.8.5"
30
+ "aipctl-darwin-x64": "2026.9.1",
31
+ "aipctl-darwin-arm64": "2026.9.1",
32
+ "aipctl-linux-x64": "2026.9.1",
33
+ "aipctl-linux-arm64": "2026.9.1",
34
+ "aipctl-windows-x64": "2026.9.1",
35
+ "aipctl-windows-arm64": "2026.9.1"
36
36
  }
37
37
  }