aipctl 2026.9.7 → 2026.9.9

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/build/index.cjs CHANGED
@@ -778,7 +778,7 @@ const isIterable = input => typeof input === "string" || hasProperty(input, Symb
778
778
  * @since 2.0.0
779
779
  * @see isObject
780
780
  */
781
- const isRecord$3 = input => isRecordOrArray(input) && !Array.isArray(input);
781
+ const isRecord$2 = input => isRecordOrArray(input) && !Array.isArray(input);
782
782
  /**
783
783
  * A refinement that checks if a value is `PromiseLike`. It performs a duck-typing
784
784
  * check for a `.then` method.
@@ -25158,7 +25158,7 @@ const go = (ast, isDecoding) => {
25158
25158
  const concurrency = getConcurrency(ast);
25159
25159
  const batching = getBatching(ast);
25160
25160
  return (input, options) => {
25161
- if (!isRecord$3(input)) {
25161
+ if (!isRecord$2(input)) {
25162
25162
  return left(new Type(ast, input));
25163
25163
  }
25164
25164
  const allErrors = options?.errors === "all";
@@ -89670,7 +89670,7 @@ 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.9.7".length > 0 ? "2026.9.7" : "0.0.0";
89673
+ const VERSION = "2026.9.9".length > 0 ? "2026.9.9" : "0.0.0";
89674
89674
  const BUILD_ENV = injectedBuildEnv;
89675
89675
  const INCLUDE_NATIVE_LOGS = BUILD_ENV !== "prod";
89676
89676
 
@@ -89804,6 +89804,14 @@ const normalizeAipArgs = (args) => {
89804
89804
  };
89805
89805
 
89806
89806
  const implementedCommandOperationIds = /* @__PURE__ */ new Set([
89807
+ "listCliAgents",
89808
+ "getCliAgent",
89809
+ "listCliAgentTemplates",
89810
+ "createCliAgent",
89811
+ "updateCliAgent",
89812
+ "deleteCliAgent",
89813
+ "updateCliAgentStatus",
89814
+ "getCliAgentShare",
89807
89815
  "getCliMe",
89808
89816
  "getCliUserSettings",
89809
89817
  "listCliOrganizations",
@@ -89814,6 +89822,14 @@ const implementedCommandOperationIds = /* @__PURE__ */ new Set([
89814
89822
  "createCliModelChatExecution"
89815
89823
  ]);
89816
89824
  const releaseEnabledCommandOperationIds = /* @__PURE__ */ new Set([
89825
+ "listCliAgents",
89826
+ "getCliAgent",
89827
+ "listCliAgentTemplates",
89828
+ "createCliAgent",
89829
+ "updateCliAgent",
89830
+ "deleteCliAgent",
89831
+ "updateCliAgentStatus",
89832
+ "getCliAgentShare",
89817
89833
  "getCliMe",
89818
89834
  "getCliUserSettings",
89819
89835
  "listCliOrganizations",
@@ -90634,14 +90650,14 @@ class InvalidAppUrlError extends Error {
90634
90650
  this.rawValue = rawValue;
90635
90651
  }
90636
90652
  }
90637
- function isRecord$2(value) {
90653
+ function isRecord$1(value) {
90638
90654
  return typeof value === "object" && value !== null;
90639
90655
  }
90640
90656
  function optionalString$1(value) {
90641
90657
  return typeof value === "string" ? value : void 0;
90642
90658
  }
90643
90659
  function parseCurrentOrganization(value) {
90644
- if (!isRecord$2(value)) {
90660
+ if (!isRecord$1(value)) {
90645
90661
  return void 0;
90646
90662
  }
90647
90663
  const id = optionalString$1(value["id"]);
@@ -90653,7 +90669,7 @@ function parseCurrentOrganization(value) {
90653
90669
  return { id, slug, name };
90654
90670
  }
90655
90671
  function normalizeLoadedConfig(value) {
90656
- if (!isRecord$2(value)) {
90672
+ if (!isRecord$1(value)) {
90657
90673
  return {};
90658
90674
  }
90659
90675
  return {
@@ -92887,11 +92903,11 @@ const CREDENTIALS_FILE = path__namespace.join(CONFIG_DIR$1, "credentials.json");
92887
92903
  const CREDENTIALS_LOCK_FILE = path__namespace.join(CONFIG_DIR$1, "credentials.lock");
92888
92904
  class CredentialServiceTag extends Tag$1("CredentialService")() {
92889
92905
  }
92890
- function isNonEmptyString$1(value) {
92906
+ function isNonEmptyString(value) {
92891
92907
  return typeof value === "string" && value.length > 0;
92892
92908
  }
92893
92909
  function hasOAuthClient(credentials) {
92894
- return isNonEmptyString$1(credentials?.clientId);
92910
+ return isNonEmptyString(credentials?.clientId);
92895
92911
  }
92896
92912
  function isAccessTokenExpired(credentials, bufferMs = 0, now = Date.now()) {
92897
92913
  if (!credentials?.expiresAt) {
@@ -92900,13 +92916,13 @@ function isAccessTokenExpired(credentials, bufferMs = 0, now = Date.now()) {
92900
92916
  return credentials.expiresAt - bufferMs <= now;
92901
92917
  }
92902
92918
  function hasValidRefreshToken(credentials, now) {
92903
- return isNonEmptyString$1(credentials.refreshToken) && (credentials.refreshExpiresAt === void 0 || credentials.refreshExpiresAt > now);
92919
+ return isNonEmptyString(credentials.refreshToken) && (credentials.refreshExpiresAt === void 0 || credentials.refreshExpiresAt > now);
92904
92920
  }
92905
92921
  function getCredentialAuthStatus(credentials, now = Date.now(), options = {}) {
92906
92922
  if (!hasOAuthClient(credentials)) {
92907
92923
  return "missing";
92908
92924
  }
92909
- const needsAccessRefresh = options.forceRefresh || !isNonEmptyString$1(credentials.accessToken) || isAccessTokenExpired(credentials, options.accessExpiryBufferMs ?? 0, now);
92925
+ const needsAccessRefresh = options.forceRefresh || !isNonEmptyString(credentials.accessToken) || isAccessTokenExpired(credentials, options.accessExpiryBufferMs ?? 0, now);
92910
92926
  if (!needsAccessRefresh) {
92911
92927
  return "authenticated";
92912
92928
  }
@@ -93902,7 +93918,7 @@ function trimTrailingSlash(value) {
93902
93918
  function toError$1(error, fallbackMessage) {
93903
93919
  return error instanceof Error ? new Error(maskSensitiveText(error.message)) : new Error(maskSensitiveText(fallbackMessage));
93904
93920
  }
93905
- function isRecord$1(value) {
93921
+ function isRecord(value) {
93906
93922
  return typeof value === "object" && value !== null;
93907
93923
  }
93908
93924
  function optionalString(value) {
@@ -93912,7 +93928,7 @@ function optionalNumber(value) {
93912
93928
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
93913
93929
  }
93914
93930
  function parseOAuthTokenResponse(value) {
93915
- if (!isRecord$1(value)) {
93931
+ if (!isRecord(value)) {
93916
93932
  throw new OAuthRequestError("OAuth token response was not a JSON object", void 0, "invalid_token_response");
93917
93933
  }
93918
93934
  const accessToken = optionalString(value["access_token"]);
@@ -94441,55 +94457,6 @@ async function acquire() {
94441
94457
  }
94442
94458
  }
94443
94459
 
94444
- const SERVER_CAPABILITY_SCHEMA_VERSION = 1;
94445
- class ServerCapabilityError extends Error {
94446
- code;
94447
- constructor(code, message) {
94448
- super(message);
94449
- this.name = "ServerCapabilityError";
94450
- this.code = code;
94451
- }
94452
- }
94453
- function parseEnabledServerOperations(value) {
94454
- if (!isRecord(value) || value["schemaVersion"] !== SERVER_CAPABILITY_SCHEMA_VERSION) {
94455
- throw new ServerCapabilityError(
94456
- "CLI_CAPABILITIES_INCOMPATIBLE",
94457
- "The server returned an unsupported CLI capability schema."
94458
- );
94459
- }
94460
- const enabledOperations = value["enabledOperations"];
94461
- if (!Array.isArray(enabledOperations)) {
94462
- throw new ServerCapabilityError(
94463
- "CLI_CAPABILITIES_INCOMPATIBLE",
94464
- "The server returned an invalid CLI capability operation list."
94465
- );
94466
- }
94467
- const operationIds = /* @__PURE__ */ new Set();
94468
- for (const operation of enabledOperations) {
94469
- if (!isRecord(operation) || !isNonEmptyString(operation["operationId"]) || !isNonEmptyString(operation["capability"]) || operationIds.has(operation["operationId"])) {
94470
- throw new ServerCapabilityError(
94471
- "CLI_CAPABILITIES_INCOMPATIBLE",
94472
- "The server returned an invalid CLI capability operation."
94473
- );
94474
- }
94475
- operationIds.add(operation["operationId"]);
94476
- }
94477
- return operationIds;
94478
- }
94479
- function requireEnabledServerOperation(enabledOperations, operationId) {
94480
- if (enabledOperations.has(operationId)) return;
94481
- throw new ServerCapabilityError(
94482
- "CLI_OPERATION_NOT_ENABLED",
94483
- `CLI operation "${operationId}" is not enabled by the server.`
94484
- );
94485
- }
94486
- function isRecord(value) {
94487
- return typeof value === "object" && value !== null && !Array.isArray(value);
94488
- }
94489
- function isNonEmptyString(value) {
94490
- return typeof value === "string" && value.length > 0;
94491
- }
94492
-
94493
94460
  class ApiError extends TaggedError$1("ApiError") {
94494
94461
  }
94495
94462
  class NotLoggedInError extends TaggedError$1("NotLoggedInError") {
@@ -94654,26 +94621,31 @@ async function toApiError(response) {
94654
94621
  errorData = await response.json();
94655
94622
  } catch {
94656
94623
  }
94657
- const details = isCliQuotaErrorCode(errorData.code) ? parseCliQuotaErrorDetails(errorData.details) : void 0;
94658
- const retryAfterSeconds = details ? parseRetryAfterSeconds(response.headers.get("retry-after")) : void 0;
94624
+ const code = errorData.code ?? errorData.errorCode;
94625
+ const details = isCliQuotaErrorCode(code) ? parseCliQuotaErrorDetails(errorData.details, code) : void 0;
94626
+ const retryAfterSeconds = details ? details.limitKind === "concurrent" ? void 0 : details.retryAfterSeconds ?? parseRetryAfterSeconds(response.headers.get("retry-after")) : void 0;
94659
94627
  return createApiError({
94660
94628
  message: errorData.message || `HTTP ${response.status}: ${response.statusText}`,
94661
94629
  statusCode: response.status,
94662
- code: errorData.code,
94630
+ code,
94663
94631
  ...details ? { details } : {},
94664
94632
  ...retryAfterSeconds === void 0 ? {} : { retryAfterSeconds }
94665
94633
  });
94666
94634
  }
94667
94635
  function isCliQuotaErrorCode(code) {
94668
- return code === "CLI_QUOTA_EXCEEDED" || code === "CLI_QUOTA_POLICY_UNAVAILABLE" || code === "CLI_QUOTA_LIMITER_UNAVAILABLE";
94636
+ return code === "CLI_QUOTA_EXCEEDED" || code === "CLI_QUOTA_POLICY_UNAVAILABLE" || code === "CLI_QUOTA_LIMITER_UNAVAILABLE" || code === "CLI_TEXT_DAILY_LIMIT" || code === "CLI_TEXT_USER_CONCURRENCY_LIMIT" || code === "CLI_TEXT_ORGANIZATION_CONCURRENCY_LIMIT";
94637
+ }
94638
+ function isCliTextQuotaErrorCode(code) {
94639
+ return code === "CLI_TEXT_DAILY_LIMIT" || code === "CLI_TEXT_USER_CONCURRENCY_LIMIT" || code === "CLI_TEXT_ORGANIZATION_CONCURRENCY_LIMIT";
94669
94640
  }
94670
- function parseCliQuotaErrorDetails(value) {
94641
+ function parseCliQuotaErrorDetails(value, code) {
94671
94642
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
94672
94643
  const details = value;
94673
- if (details["limitKind"] !== "request_rate") return void 0;
94674
- if (typeof details["quotaClass"] !== "string" || !["bootstrap", "metadata", "text-execution", "control-read"].includes(details["quotaClass"])) {
94675
- return void 0;
94676
- }
94644
+ const sourceLimitKind = details["limitKind"];
94645
+ const limitKind = sourceLimitKind === "request_rate" ? "request_rate" : sourceLimitKind === "dailyTextAdmissions" ? "daily" : sourceLimitKind === "concurrentTextExecutions" ? "concurrent" : void 0;
94646
+ if (!limitKind) return void 0;
94647
+ const quotaClass = details["quotaClass"] ?? (isCliTextQuotaErrorCode(code) ? "text-execution" : void 0);
94648
+ if (typeof quotaClass !== "string" || !["bootstrap", "metadata", "text-execution", "control-read"].includes(quotaClass)) return void 0;
94677
94649
  if (typeof details["requestId"] !== "string" || details["requestId"].length === 0) return void 0;
94678
94650
  const scope = details["scope"];
94679
94651
  if (scope !== void 0 && (typeof scope !== "string" || !["user", "organization", "bootstrap", "control-read"].includes(scope))) {
@@ -94683,17 +94655,42 @@ function parseCliQuotaErrorDetails(value) {
94683
94655
  if (resetAt !== void 0 && (typeof resetAt !== "string" || !Number.isFinite(Date.parse(resetAt)))) {
94684
94656
  return void 0;
94685
94657
  }
94658
+ const retryAfterSeconds = details["retryAfterSeconds"];
94659
+ if (retryAfterSeconds !== void 0 && (typeof retryAfterSeconds !== "number" || !Number.isSafeInteger(retryAfterSeconds) || retryAfterSeconds < 0)) {
94660
+ return void 0;
94661
+ }
94686
94662
  const dependency = details["dependency"];
94687
94663
  if (dependency !== void 0 && dependency !== "policy" && dependency !== "limiter") return void 0;
94664
+ if (!isCliQuotaCombinationValid(code, limitKind, quotaClass, scope, dependency)) return void 0;
94688
94665
  return {
94689
- limitKind: "request_rate",
94690
- quotaClass: details["quotaClass"],
94666
+ limitKind,
94667
+ quotaClass,
94691
94668
  requestId: details["requestId"],
94692
94669
  ...scope === void 0 ? {} : { scope },
94693
- ...resetAt === void 0 ? {} : { resetAt },
94670
+ ...resetAt === void 0 || limitKind === "concurrent" ? {} : { resetAt },
94671
+ ...retryAfterSeconds === void 0 || limitKind === "concurrent" ? {} : { retryAfterSeconds },
94694
94672
  ...dependency === void 0 ? {} : { dependency }
94695
94673
  };
94696
94674
  }
94675
+ function isCliQuotaCombinationValid(code, limitKind, quotaClass, scope, dependency) {
94676
+ if (isCliTextQuotaErrorCode(code)) {
94677
+ const expectedLimitKind = code === "CLI_TEXT_DAILY_LIMIT" ? "daily" : "concurrent";
94678
+ const expectedScope = code === "CLI_TEXT_ORGANIZATION_CONCURRENCY_LIMIT" ? "organization" : "user";
94679
+ return limitKind === expectedLimitKind && quotaClass === "text-execution" && scope === expectedScope && dependency === void 0;
94680
+ }
94681
+ if (limitKind !== "request_rate") return false;
94682
+ const validScope = scope === void 0 || (quotaClass === "bootstrap" ? scope === "bootstrap" : quotaClass === "control-read" ? scope === "control-read" : scope === "user" || scope === "organization");
94683
+ if (!validScope) return false;
94684
+ switch (code) {
94685
+ case "CLI_QUOTA_EXCEEDED":
94686
+ return dependency === void 0;
94687
+ case "CLI_QUOTA_POLICY_UNAVAILABLE":
94688
+ return dependency === void 0 || dependency === "policy";
94689
+ case "CLI_QUOTA_LIMITER_UNAVAILABLE":
94690
+ return dependency === void 0 || dependency === "limiter";
94691
+ }
94692
+ return false;
94693
+ }
94697
94694
  function parseRetryAfterSeconds(value) {
94698
94695
  if (!value || !/^\d+$/.test(value)) return void 0;
94699
94696
  const parsed = Number(value);
@@ -94952,7 +94949,7 @@ const makeApiServiceLive = (buildEnv) => effect$1(
94952
94949
  });
94953
94950
  const baseUrl = () => loadConfiguredApiBaseUrl();
94954
94951
  const chatBaseUrl = () => loadConfiguredChatBaseUrl();
94955
- const requireServerOperation = (operationId, token) => {
94952
+ const requireReleaseEnabledOperation = (operationId) => {
94956
94953
  if (buildEnv !== "prod") return _void;
94957
94954
  if (!releaseEnabledCommandOperationIds.has(operationId)) {
94958
94955
  return fail$3(createApiError({
@@ -94961,24 +94958,7 @@ const makeApiServiceLive = (buildEnv) => effect$1(
94961
94958
  code: "CLI_OPERATION_NOT_ENABLED"
94962
94959
  }));
94963
94960
  }
94964
- return gen(function* () {
94965
- const response = yield* makeRequest(
94966
- baseUrl,
94967
- "/api/cli/v1/capabilities",
94968
- token
94969
- );
94970
- yield* try_({
94971
- try: () => {
94972
- const enabledOperations = parseEnabledServerOperations(response);
94973
- requireEnabledServerOperation(enabledOperations, operationId);
94974
- },
94975
- catch: (error) => createApiError({
94976
- message: error instanceof Error ? error.message : "CLI server capability validation failed",
94977
- statusCode: error instanceof ServerCapabilityError && error.code === "CLI_OPERATION_NOT_ENABLED" ? 404 : 503,
94978
- code: error instanceof ServerCapabilityError ? error.code : "CLI_CAPABILITIES_INCOMPATIBLE"
94979
- })
94980
- });
94981
- });
94961
+ return _void;
94982
94962
  };
94983
94963
  const subscribeAgentChatStream = (orgSlug, conversationId, token) => gen(function* () {
94984
94964
  const resolvedChatBaseUrl = yield* requireChatBaseUrlEffect();
@@ -95046,12 +95026,12 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95046
95026
  return {
95047
95027
  listOrganizations: () => gen(function* () {
95048
95028
  const token = yield* getAccessToken();
95049
- yield* requireServerOperation("listCliOrganizations", token);
95029
+ yield* requireReleaseEnabledOperation("listCliOrganizations");
95050
95030
  return yield* makeRequest(baseUrl, "/api/cli/v1/organizations", token);
95051
95031
  }),
95052
95032
  getOrganization: (slug) => gen(function* () {
95053
95033
  const token = yield* getAccessToken();
95054
- yield* requireServerOperation("listCliOrganizations", token);
95034
+ yield* requireReleaseEnabledOperation("listCliOrganizations");
95055
95035
  const orgs = yield* makeRequest(baseUrl, "/api/cli/v1/organizations", token);
95056
95036
  const org = orgs.find((o) => o.slug === slug);
95057
95037
  if (!org) {
@@ -95291,7 +95271,8 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95291
95271
  // Phase 2: Agent management
95292
95272
  listInstalledAgents: (orgSlug) => gen(function* () {
95293
95273
  const token = yield* getAccessToken();
95294
- const path = `/api/web/organizations/slug/${encodeURIComponent(orgSlug)}/installed-agents`;
95274
+ yield* requireReleaseEnabledOperation("listCliAgents");
95275
+ const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/agents`;
95295
95276
  const raw = yield* makeRequest(baseUrl, path, token);
95296
95277
  return {
95297
95278
  pagination: normalizePageNumberPagination(raw.pagination, raw.list.length),
@@ -95300,7 +95281,8 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95300
95281
  }),
95301
95282
  getInstalledAgent: (orgSlug, agentId) => gen(function* () {
95302
95283
  const token = yield* getAccessToken();
95303
- const path = `/api/web/organizations/slug/${encodeURIComponent(orgSlug)}/installed-agents/${encodeURIComponent(agentId)}`;
95284
+ yield* requireReleaseEnabledOperation("getCliAgent");
95285
+ const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/agents/${encodeURIComponent(agentId)}`;
95304
95286
  const raw = yield* makeRequest(baseUrl, path, token);
95305
95287
  return {
95306
95288
  data: {
@@ -95312,7 +95294,8 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95312
95294
  }),
95313
95295
  updateInstalledAgentStatus: (orgSlug, agentId, status) => gen(function* () {
95314
95296
  const token = yield* getAccessToken();
95315
- const path = `/api/web/admin/organizations/slug/${encodeURIComponent(orgSlug)}/installed-agents/${encodeURIComponent(agentId)}/status`;
95297
+ yield* requireReleaseEnabledOperation("updateCliAgentStatus");
95298
+ const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/agents/${encodeURIComponent(agentId)}/status`;
95316
95299
  return yield* makeRequest(baseUrl, path, token, {
95317
95300
  method: "PATCH",
95318
95301
  body: { status }
@@ -95391,7 +95374,7 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95391
95374
  // Phase 4: Monitoring
95392
95375
  listOrgMembers: (orgSlug) => gen(function* () {
95393
95376
  const token = yield* getAccessToken();
95394
- yield* requireServerOperation("listCliOrganizationMembers", token);
95377
+ yield* requireReleaseEnabledOperation("listCliOrganizationMembers");
95395
95378
  const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/members`;
95396
95379
  const raw = yield* makeRequest(baseUrl, path, token);
95397
95380
  return {
@@ -95407,7 +95390,7 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95407
95390
  listModels: (orgSlug) => gen(function* () {
95408
95391
  yield* requireChatBaseUrlEffect();
95409
95392
  const token = yield* getAccessToken();
95410
- yield* requireServerOperation("listCliModels", token);
95393
+ yield* requireReleaseEnabledOperation("listCliModels");
95411
95394
  const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/models`;
95412
95395
  const response = yield* makeRequest(chatBaseUrl, path, token);
95413
95396
  return response.models.map((id) => ({
@@ -95418,14 +95401,14 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95418
95401
  listModelExecModels: (orgSlug) => gen(function* () {
95419
95402
  yield* requireChatBaseUrlEffect();
95420
95403
  const token = yield* getAccessToken();
95421
- yield* requireServerOperation("listCliModelExecModels", token);
95404
+ yield* requireReleaseEnabledOperation("listCliModelExecModels");
95422
95405
  const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/model-exec/models`;
95423
95406
  return yield* makeRequest(chatBaseUrl, path, token);
95424
95407
  }),
95425
95408
  executeModelExecChat: (orgSlug, body) => gen(function* () {
95426
95409
  yield* requireChatBaseUrlEffect();
95427
95410
  const token = yield* getAccessToken();
95428
- yield* requireServerOperation("createCliModelChatExecution", token);
95411
+ yield* requireReleaseEnabledOperation("createCliModelChatExecution");
95429
95412
  const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/model-exec/chat`;
95430
95413
  return yield* makeRequest(chatBaseUrl, path, token, {
95431
95414
  method: "POST",
@@ -95567,7 +95550,7 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95567
95550
  // Phase 6: Auth enhancements
95568
95551
  getMe: () => gen(function* () {
95569
95552
  const token = yield* getAccessToken();
95570
- yield* requireServerOperation("getCliMe", token);
95553
+ yield* requireReleaseEnabledOperation("getCliMe");
95571
95554
  return yield* makeRequest(baseUrl, "/api/cli/v1/me", token);
95572
95555
  }),
95573
95556
  refreshToken: () => refreshAccessToken({ force: true }),
@@ -95605,7 +95588,8 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95605
95588
  // Phase 8: Agent admin CRUD
95606
95589
  createInstalledAgent: (orgSlug, body) => gen(function* () {
95607
95590
  const token = yield* getAccessToken();
95608
- const path = `/api/web/admin/organizations/slug/${encodeURIComponent(orgSlug)}/installed-agents`;
95591
+ yield* requireReleaseEnabledOperation("createCliAgent");
95592
+ const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/agents`;
95609
95593
  return yield* makeRequest(baseUrl, path, token, {
95610
95594
  method: "POST",
95611
95595
  body
@@ -95613,7 +95597,8 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95613
95597
  }),
95614
95598
  updateInstalledAgent: (orgSlug, agentId, body) => gen(function* () {
95615
95599
  const token = yield* getAccessToken();
95616
- const path = `/api/web/admin/organizations/slug/${encodeURIComponent(orgSlug)}/installed-agents/${encodeURIComponent(agentId)}`;
95600
+ yield* requireReleaseEnabledOperation("updateCliAgent");
95601
+ const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/agents/${encodeURIComponent(agentId)}`;
95617
95602
  return yield* makeRequest(baseUrl, path, token, {
95618
95603
  method: "PATCH",
95619
95604
  body
@@ -95621,7 +95606,8 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95621
95606
  }),
95622
95607
  deleteInstalledAgent: (orgSlug, agentId) => gen(function* () {
95623
95608
  const token = yield* getAccessToken();
95624
- const path = `/api/web/admin/organizations/slug/${encodeURIComponent(orgSlug)}/installed-agents/${encodeURIComponent(agentId)}`;
95609
+ yield* requireReleaseEnabledOperation("deleteCliAgent");
95610
+ const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/agents/${encodeURIComponent(agentId)}`;
95625
95611
  return yield* makeRequest(baseUrl, path, token, { method: "DELETE" });
95626
95612
  }),
95627
95613
  exportInstalledAgent: (orgSlug, agentId) => gen(function* () {
@@ -95647,7 +95633,8 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95647
95633
  }),
95648
95634
  getInstalledAgentShare: (orgSlug, agentId) => gen(function* () {
95649
95635
  const token = yield* getAccessToken();
95650
- const path = `/api/web/admin/organizations/slug/${encodeURIComponent(orgSlug)}/installed-agents/${encodeURIComponent(agentId)}/share`;
95636
+ yield* requireReleaseEnabledOperation("getCliAgentShare");
95637
+ const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/agents/${encodeURIComponent(agentId)}/share`;
95651
95638
  const raw = yield* makeRequest(baseUrl, path, token);
95652
95639
  const sharedUserList = yield* try_({
95653
95640
  try: () => normalizeSharedUserList(raw.sharedUserList),
@@ -95660,11 +95647,19 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95660
95647
  }),
95661
95648
  listAgentTemplates: (orgSlug) => gen(function* () {
95662
95649
  const token = yield* getAccessToken();
95663
- const path = `/api/web/admin/organizations/slug/${encodeURIComponent(orgSlug)}/agents`;
95664
- const raw = yield* makeRequest(baseUrl, path, token);
95650
+ yield* requireReleaseEnabledOperation("listCliAgentTemplates");
95651
+ const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/agents/templates`;
95652
+ const templates = [];
95653
+ let page = 1;
95654
+ while (true) {
95655
+ const raw = yield* makeRequest(baseUrl, page === 1 ? path : `${path}?page=${page}`, token);
95656
+ templates.push(...raw.list);
95657
+ if (raw.list.length === 0 || page * raw.pagination.pageSize >= raw.pagination.total) break;
95658
+ page += 1;
95659
+ }
95665
95660
  return {
95666
- pagination: { pageNumber: 1, pageSize: raw.length, totalCount: raw.length },
95667
- data: raw.map((item) => ({
95661
+ pagination: { pageNumber: 1, pageSize: templates.length, totalCount: templates.length },
95662
+ data: templates.map((item) => ({
95668
95663
  id: item.agentId,
95669
95664
  displayName: item.label,
95670
95665
  categories: item.categoryList,
@@ -95749,13 +95744,13 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95749
95744
  // Org capabilities & user settings
95750
95745
  getOrgCapabilities: (orgSlug) => gen(function* () {
95751
95746
  const token = yield* getAccessToken();
95752
- yield* requireServerOperation("getCliOrganizationCapabilities", token);
95747
+ yield* requireReleaseEnabledOperation("getCliOrganizationCapabilities");
95753
95748
  const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/capabilities`;
95754
95749
  return yield* makeRequest(baseUrl, path, token);
95755
95750
  }),
95756
95751
  getUserSettings: () => gen(function* () {
95757
95752
  const token = yield* getAccessToken();
95758
- yield* requireServerOperation("getCliUserSettings", token);
95753
+ yield* requireReleaseEnabledOperation("getCliUserSettings");
95759
95754
  return yield* makeRequest(baseUrl, "/api/cli/v1/me/settings", token);
95760
95755
  }),
95761
95756
  // Tunnel management
@@ -49,8 +49,9 @@
49
49
  | SKILL-04 | skill-admin | `aipctl admin skill list`, `get`, `delete`, `publish`, `unpublish` | `listAdminSkills`, `getAdminSkill`, `deleteAdminSkill`, `updateAdminSkill` | api | `GET`, `GET`, `DELETE`, `PATCH` | `/api/web/admin/organizations/slug/{organizationSlug}/skills`; `/skills/{skillId}` | list query `{ pageNumber,pageSize,search }`; publish toggle body `{ availableInLibrary: boolean }` | local `ListAdminSkillsResponse`, `SkillDetail`, `void` | `resource/oas/modules/backend/organization/skills/openapi.yaml` | `frontend/packages/api/src/out/modules/organization-skills-types.ts#paths["/api/web/admin/organizations/slug/{organizationSlug}/skills"]`, `#paths["/api/web/admin/organizations/slug/{organizationSlug}/skills/{skillId}"]` | `generated-ready` | N/A | `keep` | none | N/A | existing `commands/admin/skill.test.ts`; needed admin list/detail/update path snapshot |
50
50
  | SKILL-05 | skill-admin | `aipctl skill download`; `aipctl admin skill upload`, `aipctl admin skill download`, `aipctl admin skill github inspect/import/sync/credentials set/clear` | `createAdminUploadUrl` → `uploadToS3` → `completeAdminUpload` (`abortAdminUpload` on failure); `getSkillDownloadUrl` → `downloadFromUrl`; `inspectGitHubSkillSource`, `prepareGitHubSkill`, `syncPrepareGitHubSkill`, `updateGitHubSkillCredentials` | api + external | `POST`/`PUT`/`POST`; `POST`/`GET`; `POST`, `POST`, `POST`, `PATCH` | `/admin/.../skills/upload-url`; `/admin/.../skills/download-url`; `/admin/.../skills/github-repo-inspect`; `/github-prepare`; `/{skillId}/sync-prepare`; `/{skillId}/github-credentials` | upload/download local bodies; GitHub inspect/prepare `{ url, pat? }`; credentials `{ pat or null }` | upload/download/GitHub admin skill DTOs | `resource/oas/modules/backend/organization/skills/openapi.yaml` | `frontend/packages/api/src/out/modules/organization-skills-types.ts#paths["/api/web/admin/organizations/slug/{organizationSlug}/skills/upload-url"]`, `#paths["/api/web/admin/organizations/slug/{organizationSlug}/skills/download-url"]`, `#paths["/api/web/admin/organizations/slug/{organizationSlug}/skills/github-repo-inspect"]`, `#paths["/api/web/admin/organizations/slug/{organizationSlug}/skills/github-prepare"]`, `#paths["/api/web/admin/organizations/slug/{organizationSlug}/skills/{skillId}/sync-prepare"]`, `#paths["/api/web/admin/organizations/slug/{organizationSlug}/skills/{skillId}/github-credentials"]` | `external-presigned` | N/A | `keep` | none | N/A | existing `commands/admin/skill.test.ts`, `commands/skill/skill-extended.test.ts`; needed GitHub admin skill contract fixtures |
51
51
  | P1-SKILL-01 | skill-docs | `aipctl skill get --json`, `aipctl admin skill get --json`, `aipctl admin skill github import/sync --json` | same owners as `SKILL-01`/`SKILL-05` | api | same as upstream rows | same as upstream rows | runtime/JSON/help/docs now align on `source`, `origin`, `updateAvailable`, `secretsAvailable`, GitHub metadata, `secretKeys`; stale `version` and file `size` removed | JSON/help/docs contract for skill objects | `resource/oas/modules/backend/organization/skills/openapi.yaml` | `frontend/packages/api/src/out/modules/organization-skills-types.ts#components[...]` | `generated-ready` | `P1` | `keep` | none | N/A | updated `src/core/api.test.ts`, `commands/skill/skill.test.ts`, `commands/skill/skill-extended.test.ts`, `commands/admin/skill.test.ts`, `commands/json-format.test.ts`, `../docs/how-to/aip-cli-usage.md`; covers JSON/help/docs field sync |
52
- | P0-AGENT-01 | agent-user | `aipctl agent list`, `aipctl agent get` | `listInstalledAgents`, `getInstalledAgent` | api | `GET`, `GET` | `/api/web/organizations/slug/{organizationSlug}/installed-agents`; `/installed-agents/{installedAgentId}` | legacy implemented endpoint maps `list`/`installedAgentId` into CLI `data`/`id`; detail wraps legacy response in `{ data }` and maps `installedIntegrationList` fallback | local `ListInstalledAgentsResponse`, `{ data: InstalledAgentDetail }` | implemented: `resource/oas/duplo.yaml`; future v2 source: `resource/oas/modules/backend/agent/user/openapi.yaml` | legacy `frontend/packages/api/src/out/user-types.ts#paths["/api/web/organizations/slug/{organizationSlug}/installed-agents"]`, `#paths["/api/web/organizations/slug/{organizationSlug}/installed-agents/{installedAgentId}"]` | `legacy-generated` | `P0` | `keep legacy` | owner=`backend app AgentsApiController`; rationale=`agent v2 controllers currently throw NotImplementedError in this repo, so public beta must call implemented legacy endpoints`; removal condition=`AgentUserApiController v2 methods are implemented and deployed`; incorporation plan=`switch to agent v2 OAS/generated contract only after backend implementation lands` | `AIP-PUB-05` | updated `src/core/api.test.ts`, `commands/agent/agent.test.ts`; covers implemented legacy list/detail path and response mapping |
53
- | P0-AGENT-02 | agent-admin | `aipctl admin agent create`, `update`, `delete`, `export`, `share`, `templates`; `aipctl agent enable`, `disable` | `createInstalledAgent`, `updateInstalledAgent`, `deleteInstalledAgent`, `exportInstalledAgent`, `getInstalledAgentShare`, `listAgentTemplates`, `updateInstalledAgentStatus` | api | `POST`, `PATCH`, `DELETE`, `GET`, `GET`, `GET`, `PATCH` | `/api/web/admin/organizations/slug/{organizationSlug}/installed-agents`; `/{installedAgentId}`; export uses legacy direct `/web/admin/organizations/slug/{organizationSlug}/installed-agents/{installedAgentId}/export`; `/share`; `/agents`; `/{installedAgentId}/status` | create/update bodies follow implemented legacy admin request (`custom.iconKeyOrUrl`, `knowledgeBundleIdList`); export is YAML bytes from direct controller path; templates map legacy `agentId`/`label`/`categoryList`; status body `{ status }` | create/update return plain `{ id }`; local admin agent DTOs + `Buffer` export | implemented: `resource/oas/duplo.yaml`; export controller path: `backend/api/src/main/kotlin/com/querypie/duplo/api/app/controller/AgentExportController.kt`; future v2 source: `resource/oas/modules/backend/agent/admin/openapi.yaml` | legacy `frontend/packages/api/src/out/user-types.ts#paths["/api/web/admin/organizations/slug/{organizationSlug}/installed-agents"]`, `#paths["/api/web/admin/organizations/slug/{organizationSlug}/installed-agents/{installedAgentId}"]`, `#paths["/api/web/admin/organizations/slug/{organizationSlug}/agents"]`; export path intentionally OAS-codegen excluded | `legacy-generated` | `P0` | `keep legacy` | owner=`backend app AgentsApiController + AgentExportController`; rationale=`agent v2 admin controllers currently throw NotImplementedError and export is implemented only at /web/admin direct controller path in PROD`; removal condition=`AgentAdminApiController v2 and export path are implemented/deployed under versioned OAS path`; incorporation plan=`switch to agent v2 OAS/generated contract only after backend implementation lands, then remove direct export exception` | `AIP-PUB-05` | updated `src/core/api.test.ts`, `commands/agent/agent.test.ts`; covers implemented legacy CRUD/status/template path/body, direct export path, Buffer response, and 404 propagation |
52
+ | P0-AGENT-01 | agent-user | `aipctl agent list`, `aipctl agent get` | `listInstalledAgents`, `getInstalledAgent` | api | `GET`, `GET` | `/api/cli/v1/organizations/{organizationSlug}/agents`; `/{installedAgentId}` | preserves user read semantics and maps `list`/`installedAgentId` to local `data`/`id` | local `ListInstalledAgentsResponse`, `{ data: InstalledAgentDetail }` | `resource/oas/modules/backend/cli/openapi.yaml`; shared DTO refs from `resource/oas/duplo.yaml` | `backend/api/.../cli/generated/api/CliAgentApi.kt` | `generated-ready` | `P0` | `migrate` | none; dedicated adapter delegates to the working `AgentsApiController` bean | `AI-6344` | `src/core/api.test.ts`, `CliAgentApiControllerTest`, Gateway registry/admission parity; real local production CLI list/get JSON |
53
+ | P0-AGENT-02 | agent-admin | `aipctl admin agent create`, `update`, `delete`, `share`, `templates`; `aipctl agent enable`, `disable` | `createInstalledAgent`, `updateInstalledAgent`, `deleteInstalledAgent`, `getInstalledAgentShare`, `listAgentTemplates`, `updateInstalledAgentStatus` | api | `POST`, `PATCH`, `DELETE`, `GET`, `GET`, `PATCH` | `/api/cli/v1/organizations/{organizationSlug}/agents`; `/{installedAgentId}`; `/{installedAgentId}/share`; `/templates`; `/{installedAgentId}/status` | existing create/update/status bodies; templates collect bounded pages and retain the full catalog; share is read-only | create/update `{ id }`, void mutation responses, existing CLI template/share mapping | `resource/oas/modules/backend/cli/openapi.yaml`; shared legacy DTO refs | `backend/api/.../cli/generated/api/CliAgentApi.kt` | `generated-ready` | `P0` | `migrate` | none; AI operator/workflow/audit policy stays in existing domain owner | `AI-6344` | all eight operations capability/path tests, mutation non-replay, template paging, real CLI lifecycle/DB/audit and metadata 429 verification |
54
+ | P0-AGENT-03 | agent-export | `aipctl admin agent export` (GA-CANDIDATE, production gated) | `exportInstalledAgent` | api | `GET` | `/web/admin/organizations/slug/{organizationSlug}/installed-agents/{installedAgentId}/export` | existing direct controller request | YAML `Buffer` | `backend/api/.../app/controller/AgentExportController.kt`; intentionally OAS-codegen excluded | none | `legacy-generated` | `P0` | `legacy-exception` | owner=`AgentExportController`; dedicated transfer contract, size/timeout/partial-file/authorization/audit gates remain | `AI-6380` | existing direct export path/Buffer/404 tests; production help remains hidden |
54
55
  | P1-CHAT-01 | agent-chat | `aipctl agent chat` | `agentNewChat`, `agentChat`, local `parseSseStream` | chat | `POST` + SSE | `/librechat/api/{orgSlug}/agent/new-chat`; `/librechat/api/{orgSlug}/agent/chat` | request body `{ agentInstallationId or conversationId, text, files: [] }` | local `AgentChatResult { text, conversationId }` parsed from SSE event fields `final`, `conversation.conversationId`, `responseMessage` | path source: `resource/oas/chat.yaml` | `frontend/packages/api/src/out/chat-types.ts#paths["/librechat/api/{orgSlug}/agent/new-chat"]`, `#paths["/librechat/api/{orgSlug}/agent/chat"]` | `stream-exception` | `P1` | `legacy-exception` | owner=`chat backend agent SSE stream contract`; rationale=`path exists in OpenAPI but final SSE event payload is still out-of-band`; removal condition=`official SSE event schema or versioned fixture is published with the chat contract`; incorporation plan=`bind CLI parser/tests to the published SSE fixture and drop local-only stream assumptions` | N/A | updated `src/core/api.test.ts`; covers content[] text, fallback text, malformed event skip, and no-final-event failure |
55
56
  | P1-AUTO-01 | automation | `aipctl automation list`, `get`, `create`, `update`, `delete`, `run`, `enable`, `disable`, `history`, `cancel`, `webhook-regen`, `triggers` | `listAutomations`, `getAutomation`, `createAutomation`, `updateAutomation`, `deleteAutomation`, `runAutomation`, `updateAutomationStatus`, `listAutomationHistories`, `cancelAutomationHistory`, `regenerateWebhookUrl`, `getTriggerProviderMetadata` | api | `GET`, `POST`, `PUT`, `DELETE` mix | `/api/web/organizations/slug/{organizationSlug}/automations...`; `/automation-trigger-providers/.../metadata` | automation pagination normalized to `{ page, pageSize, total }`; metadata calls now use provider-specific OAS paths and required args (`accountId`, `folderId`, `serviceType`) | local automation DTOs + webhook URL DTO | `resource/oas/modules/backend/automation/user/openapi.yaml` | `frontend/packages/api/src/out/modules/automation-user-types.ts#paths[...]` | `generated-ready` | `P1` | `keep` | none | N/A | updated `src/core/api.test.ts`, `commands/automation/automation.test.ts`, `commands/json-format.test.ts`; covers official pagination fields, provider-specific metadata routes, and unsupported-provider preflight failure |
56
57
  | CHAT-01 | chat-readonly | `aipctl chat list`, `aipctl chat get`, `aipctl chat messages` | `listConversations`, `getConversation`, `listMessages` | chat | `GET` | `/librechat/api/{orgSlug}/convos`; `/convos/{conversationId}`; `/messages/{conversationId}` | list sends no default query; optional cursor only (`?cursor=...`). `pageSize`/`sortBy`/`sortDirection` defaults removed due deployed Chat validation drift | local `ListConversationsResponse`, `ConversationDetail`, `ChatMessage[]` | `resource/oas/chat.yaml` | `frontend/packages/api/src/out/chat-types.ts#paths["/librechat/api/{orgSlug}/convos"]`, `#paths["/librechat/api/{orgSlug}/convos/{conversationId}"]`, `#paths["/librechat/api/{orgSlug}/messages/{conversationId}"]` | `legacy-generated` | N/A | `keep` | owner=`chat backend`; rationale=`PROD rejects CLI default pageSize/sort query while no-query and cursor-only read path are safe`; removal condition=`chat deployed contract publishes and accepts explicit pagination query for public CLI`; incorporation plan=`reintroduce explicit pagination only after OAS/source/deployed validation align` | N/A | updated `src/core/api.test.ts`, existing `commands/chat/chat.test.ts`; covers default no-query and cursor-only URL |
@@ -67,15 +68,18 @@
67
68
 
68
69
  ## 공통 CLI request quota 오류 계약
69
70
 
70
- `GA-DIRECT` 업무 호출과 `GET /api/cli/v1/capabilities`는 Gateway request-rate admission을 통과한다. Gateway-owned source 계약은 `resource/oas/modules/backend-gateway/me/components/schemas/CliQuotaErrorResponse.yaml`과 `CliQuotaErrorDetails.yaml`이다.
71
+ `GA-DIRECT` 업무 호출과 명시적 discovery/진단용 `GET /api/cli/v1/capabilities`는 각각 Gateway request-rate admission을 통과한다. production CLI는 업무마다 capability를 선조회하지 않는다. 로컬 release manifest는 artifact에 없는 명령을 차단하고, 실제 업무 endpoint의 Gateway registry가 최신 server release/rollout/quota를 authoritative하게 검사한다. Gateway-owned source 계약은 `resource/oas/modules/backend-gateway/me/components/schemas/CliQuotaErrorResponse.yaml`과 `CliQuotaErrorDetails.yaml`이다.
71
72
 
72
73
  | HTTP | code | CLI 처리 |
73
74
  | --- | --- | --- |
74
75
  | 429 | `CLI_QUOTA_EXCEEDED` | `limitKind=request_rate`, quota class, denying scope, request ID를 보존한다. 양수 refill로 계산 가능한 경우에만 `Retry-After`, `resetAt`을 보존한다. 자동 retry는 하지 않는다. |
75
76
  | 503 | `CLI_QUOTA_POLICY_UNAVAILABLE` | Backend effective-policy auth/timeout/schema/freshness 실패. stale/default policy로 대체하거나 자동 retry하지 않는다. |
76
77
  | 503 | `CLI_QUOTA_LIMITER_UNAVAILABLE` | Redis timeout/script/result 실패. 유료·mutation 요청을 자동 retry하지 않는다. |
78
+ | 429 | `CLI_TEXT_DAILY_LIMIT` | `limitKind=daily`, `quotaClass=text-execution`, 사용자 scope와 request ID를 보존한다. 유효한 UTC reset 시각과 그 시각에서 계산한 retry만 노출하며 자동 retry하지 않는다. |
79
+ | 429 | `CLI_TEXT_USER_CONCURRENCY_LIMIT` | `limitKind=concurrent`, `quotaClass=text-execution`, 사용자 scope와 request ID만 보존한다. permit 해제 시각을 알 수 없으므로 `Retry-After`와 `resetAt`을 만들거나 전달하지 않는다. |
80
+ | 429 | `CLI_TEXT_ORGANIZATION_CONCURRENCY_LIMIT` | `limitKind=concurrent`, `quotaClass=text-execution`, 조직 scope와 request ID만 보존한다. permit 해제 시각을 알 수 없으므로 `Retry-After`와 `resetAt`을 만들거나 전달하지 않는다. |
77
81
 
78
- CLI `ApiError`는 위 세 code에서 allowlist된 quota detail만 보존하며 서버가 추가로 보낸 조직/사용자 ID, raw policy, Redis key는 노출하지 않는다. 회귀 테스트는 `src/core/api.test.ts`와 Gateway `test/cli/quota/**`에 있다.
82
+ CLI `ApiError`는 위 여섯 code에서 allowlist된 quota detail만 보존하며 서버가 추가로 보낸 조직/사용자 ID, raw policy, Redis key는 노출하지 않는다. Gateway는 `code`, Chat은 `errorCode`를 사용하지만 CLI에서 같은 진단 구조로 정규화한다. 회귀 테스트는 `src/core/api.test.ts`, Chat `cli-text-execution.internal.service.spec.ts`, Gateway `test/cli/quota/**`에 있다.
79
83
 
80
84
  ## Queue IDs for follow-up
81
85
 
@@ -83,8 +87,8 @@ CLI `ApiError`는 위 세 code에서 allowlist된 quota detail만 보존하며
83
87
 
84
88
  - `P0-ORG-01` org member list/mutation legacy-exception aligned (done in `AIP-PUB-04`, independent contract follow-up remains)
85
89
  - `P0-ORG-02` org invite-link trio hidden from public beta (done in `AIP-PUB-04`)
86
- - `P0-AGENT-01` user agent remains on implemented legacy endpoints until v2 backend lands
87
- - `P0-AGENT-02` admin agent remains on implemented legacy endpoints until v2 backend lands
90
+ - `P0-AGENT-01` / `P0-AGENT-02` moved to dedicated CLI lifecycle endpoints with metadata quota and capability checks (`AI-6344`)
91
+ - `P0-AGENT-03` export remains a gated transfer candidate (`AI-6380`)
88
92
  - `P0-DRIVE-01` drive list/download/delete/mkdir/rename/usage contract migration (done in `AIP-PUB-04`)
89
93
  - `P0-DRIVE-02` drive upload contract migration (done in `AIP-PUB-04`)
90
94
  - `P0-MODEL-01` model list path/DTO migration (done in `AIP-PUB-04`)
@@ -101,6 +105,6 @@ CLI `ApiError`는 위 세 code에서 allowlist된 quota detail만 보존하며
101
105
  ## Manual notes
102
106
 
103
107
  - Generated reference files under `frontend/packages/api/src/out/**` are inventory links only. They are not imported by CLI source/build/test/lint.
104
- - `agent-user` / `agent-admin` modern OAS files exist under `resource/oas/modules/backend/agent/**`, but current backend v2 controllers throw `NotImplementedError`; current CLI intentionally uses implemented `AgentsApiController` endpoints until v2 implementation lands.
108
+ - Agent lifecycle uses the dedicated `CliAgentApiController` and CLI OAS, delegating internally to the implemented `AgentsApiController` bean. Future v2 implementation can replace that internal owner without changing the CLI endpoint. `agent chat` and `admin agent export` retain their separate candidate gates.
105
109
  - OAuth authorization-server metadata, dynamic client registration, authorize, and token endpoints used by `src/core/oauth.ts` were not found in current `resource/oas`; they need owner-backed contractization.
106
110
  - `config` commands intentionally excluded. local config surface has no network owner.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aipctl",
3
- "version": "2026.9.7",
3
+ "version": "2026.9.9",
4
4
  "description": "QueryPie AIP and MCP CLI tool",
5
5
  "author": "QueryPie",
6
6
  "license": "UNLICENSED",
@@ -22,8 +22,8 @@
22
22
  "node": ">=24.18.0"
23
23
  },
24
24
  "metadata": {
25
- "contentHash": "193ccba3e435808c7b5d0088f7fa877f90dee7b22206ac32aaa73bc950cd77dd",
26
- "wrapperHash": "dd78d97e373451c52e871f46c154b9cec801b192cb68c250932de890891e8a97",
25
+ "contentHash": "95bfb896d5e0e21910685b39eff86f3d35ee5c1b0ef28854912f78fdbc7bee58",
26
+ "wrapperHash": "a4ef2565f44cb9eb4a3706246f59427bf40a5275dc7bea030fb30024be6f5ab8",
27
27
  "nativeHash": "006b08c3e391f4571d9b7c0f12d684fe2448106259e7e0581b10f9be362452e9"
28
28
  },
29
29
  "optionalDependencies": {