aipctl 2026.10.4 → 2026.10.5

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
@@ -26,6 +26,7 @@ var require$$0$1 = require('buffer');
26
26
  var require$$2 = require('util');
27
27
  require('node:net');
28
28
  require('node:worker_threads');
29
+ var promises = require('node:timers/promises');
29
30
  var require$$0$6 = require('path');
30
31
  var require$$0$5 = require('fs');
31
32
  var require$$0$4 = require('constants');
@@ -89693,8 +89694,6 @@ const SESSION_EXPIRED_MESSAGE = 'Session expired. Run "aipctl auth login" to con
89693
89694
 
89694
89695
  const CONFIG_SET_HINT = "aipctl config set --app-url https://app.example.com";
89695
89696
  const CONFIG_DIR$2 = path__namespace.join(os__namespace.homedir(), ".config", "aip");
89696
- const CONFIG_FILE = path__namespace.join(CONFIG_DIR$2, "config.json");
89697
- let cachedConfig;
89698
89697
  class ConfigServiceTag extends Tag$1("ConfigService")() {
89699
89698
  }
89700
89699
  class AppUrlNotConfiguredError extends Error {
@@ -89744,9 +89743,9 @@ function cloneConfig(config) {
89744
89743
  currentOrganization: config.currentOrganization ? { ...config.currentOrganization } : void 0
89745
89744
  };
89746
89745
  }
89747
- function readConfigFile() {
89746
+ function readConfigFile(configFile) {
89748
89747
  try {
89749
- const content = fs__namespace.readFileSync(CONFIG_FILE, "utf-8");
89748
+ const content = fs__namespace.readFileSync(configFile, "utf-8");
89750
89749
  return normalizeLoadedConfig(JSON.parse(content));
89751
89750
  } catch {
89752
89751
  return {};
@@ -89831,25 +89830,33 @@ function requireApiBaseUrl(config) {
89831
89830
  function requireChatBaseUrl(config) {
89832
89831
  return deriveInternalBaseUrls(requireAppBaseUrl(config)).chatBaseUrl;
89833
89832
  }
89834
- const ConfigServiceLive = succeed$2(ConfigServiceTag, {
89835
- load: () => {
89836
- cachedConfig ??= readConfigFile();
89837
- return cloneConfig(cachedConfig);
89838
- },
89839
- save: (config, currentConfig) => {
89840
- const current = currentConfig ?? cachedConfig ?? readConfigFile();
89841
- const merged = cloneConfig({
89842
- ...current,
89843
- ...config
89844
- });
89845
- cachedConfig = merged;
89846
- if (isSameConfig(current, merged)) {
89847
- return;
89833
+ function makeConfigService(directory) {
89834
+ const configFile = path__namespace.join(directory, "config.json");
89835
+ return {
89836
+ load: () => {
89837
+ return cloneConfig(readConfigFile(configFile));
89838
+ },
89839
+ save: (config, currentConfig) => {
89840
+ const current = currentConfig ?? readConfigFile(configFile);
89841
+ const merged = cloneConfig({
89842
+ ...current,
89843
+ ...config
89844
+ });
89845
+ if (isSameConfig(current, merged)) {
89846
+ return;
89847
+ }
89848
+ fs__namespace.mkdirSync(directory, { recursive: true, mode: 448 });
89849
+ const temporaryFile = `${configFile}.${crypto.randomUUID()}.tmp`;
89850
+ try {
89851
+ fs__namespace.writeFileSync(temporaryFile, JSON.stringify(merged, null, 2), { mode: 384 });
89852
+ fs__namespace.renameSync(temporaryFile, configFile);
89853
+ } finally {
89854
+ if (fs__namespace.existsSync(temporaryFile)) fs__namespace.unlinkSync(temporaryFile);
89855
+ }
89848
89856
  }
89849
- fs__namespace.mkdirSync(CONFIG_DIR$2, { recursive: true });
89850
- fs__namespace.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2));
89851
- }
89852
- });
89857
+ };
89858
+ }
89859
+ const ConfigServiceLive = succeed$2(ConfigServiceTag, makeConfigService(CONFIG_DIR$2));
89853
89860
  const ConfigService = ConfigServiceTag;
89854
89861
 
89855
89862
  var properLockfile = {exports: {}};
@@ -91959,9 +91966,10 @@ var lockfile = /*@__PURE__*/getDefaultExportFromCjs(properLockfileExports);
91959
91966
 
91960
91967
  const ACCESS_TOKEN_FALLBACK_TTL_MS = 12 * 60 * 60 * 1e3;
91961
91968
  const REFRESH_TOKEN_FALLBACK_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
91969
+ const REFRESH_LOCK_WAIT_MS = 7e4;
91970
+ class RefreshLockWaitError extends TaggedError$1("RefreshLockWaitError") {
91971
+ }
91962
91972
  const CONFIG_DIR$1 = path__namespace.join(os__namespace.homedir(), ".config", "aip");
91963
- const CREDENTIALS_FILE = path__namespace.join(CONFIG_DIR$1, "credentials.json");
91964
- const CREDENTIALS_LOCK_FILE = path__namespace.join(CONFIG_DIR$1, "credentials.lock");
91965
91973
  class CredentialServiceTag extends Tag$1("CredentialService")() {
91966
91974
  }
91967
91975
  function isNonEmptyString(value) {
@@ -91996,28 +92004,30 @@ function hasUsableCredentialSession(credentials, now = Date.now()) {
91996
92004
  const status = getCredentialAuthStatus(credentials, now);
91997
92005
  return status === "authenticated" || status === "refreshable";
91998
92006
  }
91999
- const loadCredentials = () => {
92007
+ const loadCredentials = (directory) => {
92008
+ const credentialsFile = path__namespace.join(directory, "credentials.json");
92000
92009
  try {
92001
- if (fs__namespace.existsSync(CREDENTIALS_FILE)) {
92002
- const content = fs__namespace.readFileSync(CREDENTIALS_FILE, "utf-8");
92010
+ if (fs__namespace.existsSync(credentialsFile)) {
92011
+ const content = fs__namespace.readFileSync(credentialsFile, "utf-8");
92003
92012
  return JSON.parse(content);
92004
92013
  }
92005
92014
  } catch {
92006
92015
  }
92007
92016
  return null;
92008
92017
  };
92009
- function ensureConfigDir$1() {
92010
- fs__namespace.mkdirSync(CONFIG_DIR$1, { recursive: true });
92018
+ function ensureConfigDir$1(directory) {
92019
+ fs__namespace.mkdirSync(directory, { recursive: true, mode: 448 });
92011
92020
  }
92012
- function ensureLockFile() {
92013
- ensureConfigDir$1();
92014
- if (!fs__namespace.existsSync(CREDENTIALS_LOCK_FILE)) {
92015
- fs__namespace.writeFileSync(CREDENTIALS_LOCK_FILE, "", { mode: 384 });
92021
+ function ensureLockFile(directory, name) {
92022
+ ensureConfigDir$1(directory);
92023
+ const lockFile = path__namespace.join(directory, name);
92024
+ if (!fs__namespace.existsSync(lockFile)) {
92025
+ fs__namespace.writeFileSync(lockFile, "", { mode: 384 });
92016
92026
  }
92017
92027
  }
92018
- async function withCredentialLock(fn) {
92019
- ensureLockFile();
92020
- const release = await lockfile.lock(CREDENTIALS_LOCK_FILE, {
92028
+ async function withCredentialLock(directory, fn, name = "credentials.lock") {
92029
+ ensureLockFile(directory, name);
92030
+ const release = await lockfile.lock(path__namespace.join(directory, name), {
92021
92031
  retries: { retries: 20, minTimeout: 50, maxTimeout: 200 },
92022
92032
  stale: 5e3
92023
92033
  });
@@ -92027,26 +92037,89 @@ async function withCredentialLock(fn) {
92027
92037
  await release();
92028
92038
  }
92029
92039
  }
92030
- const CredentialServiceLive = succeed$2(CredentialServiceTag, {
92031
- load: loadCredentials,
92032
- save: (credentials) => {
92033
- ensureConfigDir$1();
92034
- fs__namespace.writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), { mode: 384 });
92035
- },
92036
- clear: () => {
92040
+ async function withRefreshLock(directory, fn, options = {}) {
92041
+ const checkCancelled = () => {
92042
+ if (options.signal?.aborted)
92043
+ throw new RefreshLockWaitError({
92044
+ code: "CLI_REQUEST_CANCELLED",
92045
+ message: "Authentication refresh wait was cancelled."
92046
+ });
92047
+ };
92048
+ checkCancelled();
92049
+ ensureLockFile(directory, "refresh.lock");
92050
+ const deadline = performance.now() + (options.timeoutMs ?? REFRESH_LOCK_WAIT_MS);
92051
+ const checkDeadline = () => {
92052
+ if (performance.now() >= deadline)
92053
+ throw new RefreshLockWaitError({
92054
+ code: "CLI_AUTH_REFRESH_WAIT_TIMEOUT",
92055
+ message: 'Timed out waiting for another command to refresh authentication. Check "aipctl auth status" before trying again.'
92056
+ });
92057
+ };
92058
+ let release;
92059
+ while (!release) {
92060
+ checkCancelled();
92061
+ checkDeadline();
92037
92062
  try {
92038
- if (fs__namespace.existsSync(CREDENTIALS_FILE)) {
92039
- fs__namespace.unlinkSync(CREDENTIALS_FILE);
92063
+ release = await lockfile.lock(path__namespace.join(directory, "refresh.lock"), { retries: 0, stale: 5e3 });
92064
+ } catch (error) {
92065
+ checkCancelled();
92066
+ if (error.code !== "ELOCKED") throw error;
92067
+ try {
92068
+ await promises.setTimeout(Math.max(0, Math.min(100, deadline - performance.now())), void 0, { signal: options.signal });
92069
+ } catch (error2) {
92070
+ checkCancelled();
92071
+ throw error2;
92040
92072
  }
92041
- } catch {
92042
92073
  }
92043
- },
92044
- isLoggedIn: () => {
92045
- const creds = loadCredentials();
92046
- return hasUsableCredentialSession(creds);
92047
- },
92048
- withLock: withCredentialLock
92049
- });
92074
+ }
92075
+ try {
92076
+ checkCancelled();
92077
+ checkDeadline();
92078
+ return await fn();
92079
+ } finally {
92080
+ await release();
92081
+ }
92082
+ }
92083
+ function makeCredentialService(directory) {
92084
+ const credentialsFile = path__namespace.join(directory, "credentials.json");
92085
+ const revisionFile = path__namespace.join(directory, "session-revision");
92086
+ return {
92087
+ load: () => loadCredentials(directory),
92088
+ getRevision: () => {
92089
+ try {
92090
+ return fs__namespace.readFileSync(revisionFile, "utf-8");
92091
+ } catch (error) {
92092
+ if (error.code === "ENOENT") return "legacy";
92093
+ throw error;
92094
+ }
92095
+ },
92096
+ save: (credentials, options) => {
92097
+ ensureConfigDir$1(directory);
92098
+ if (!options?.preserveSession) fs__namespace.writeFileSync(revisionFile, crypto.randomUUID(), { mode: 384 });
92099
+ const temporaryFile = `${credentialsFile}.${crypto.randomUUID()}.tmp`;
92100
+ try {
92101
+ fs__namespace.writeFileSync(temporaryFile, JSON.stringify(credentials, null, 2), {
92102
+ mode: 384
92103
+ });
92104
+ fs__namespace.renameSync(temporaryFile, credentialsFile);
92105
+ } finally {
92106
+ if (fs__namespace.existsSync(temporaryFile)) fs__namespace.unlinkSync(temporaryFile);
92107
+ }
92108
+ },
92109
+ clear: () => {
92110
+ ensureConfigDir$1(directory);
92111
+ fs__namespace.writeFileSync(revisionFile, crypto.randomUUID(), { mode: 384 });
92112
+ if (fs__namespace.existsSync(credentialsFile)) fs__namespace.unlinkSync(credentialsFile);
92113
+ },
92114
+ isLoggedIn: () => {
92115
+ const creds = loadCredentials(directory);
92116
+ return hasUsableCredentialSession(creds);
92117
+ },
92118
+ withLock: (fn) => withCredentialLock(directory, fn),
92119
+ withRefreshLock: (fn, options) => withRefreshLock(directory, fn, options)
92120
+ };
92121
+ }
92122
+ const CredentialServiceLive = succeed$2(CredentialServiceTag, makeCredentialService(CONFIG_DIR$1));
92050
92123
  const CredentialService = CredentialServiceTag;
92051
92124
 
92052
92125
  let isDockerCached;
@@ -93134,6 +93207,7 @@ async function performHttpResponse(input, init, consume, options) {
93134
93207
  }
93135
93208
  }
93136
93209
  controller.signal.throwIfAborted();
93210
+ options.beforeDispatch?.();
93137
93211
  dispatched = true;
93138
93212
  response = await fetch(input, { ...init, signal: controller.signal });
93139
93213
  controller.signal.throwIfAborted();
@@ -93186,6 +93260,30 @@ async function performHttpResponse(input, init, consume, options) {
93186
93260
  }
93187
93261
  }
93188
93262
 
93263
+ class AuthSessionChangedError extends TaggedError$1("AuthSessionChangedError") {
93264
+ }
93265
+ function captureAuthSession(config, credentials) {
93266
+ return {
93267
+ revision: credentials.getRevision(),
93268
+ appBaseUrl: requireAppBaseUrl(config.load())
93269
+ };
93270
+ }
93271
+ function isCurrentAuthSession(snapshot, config, credentials) {
93272
+ try {
93273
+ return credentials.getRevision() === snapshot.revision && requireAppBaseUrl(config.load()) === snapshot.appBaseUrl;
93274
+ } catch {
93275
+ return false;
93276
+ }
93277
+ }
93278
+ function assertAuthSession(snapshot, config, credentials) {
93279
+ if (!isCurrentAuthSession(snapshot, config, credentials)) {
93280
+ throw new AuthSessionChangedError({
93281
+ code: "CLI_AUTH_SESSION_CHANGED",
93282
+ message: 'Authentication or App URL changed. Check "aipctl auth status" before trying again.'
93283
+ });
93284
+ }
93285
+ }
93286
+
93189
93287
  const CALLBACK_HOST = "127.0.0.1";
93190
93288
  const CALLBACK_PORT = 8484;
93191
93289
  const CALLBACK_PATH = "/callback";
@@ -93228,6 +93326,7 @@ function trimTrailingSlash(value) {
93228
93326
  }
93229
93327
  function toError$1(error, fallbackMessage) {
93230
93328
  if (error instanceof OAuthRequestError) return error;
93329
+ if (error instanceof AuthSessionChangedError) return new OAuthRequestError(error.message, void 0, error.code);
93231
93330
  return error instanceof Error ? new Error(maskSensitiveText(error.message)) : new Error(maskSensitiveText(fallbackMessage));
93232
93331
  }
93233
93332
  function isRecord(value) {
@@ -93301,7 +93400,7 @@ async function fetchJson(input, init, parse = (value) => value) {
93301
93400
  );
93302
93401
  }
93303
93402
  }
93304
- async function discoverOAuthMetadata(apiBaseUrl) {
93403
+ async function discoverOAuthMetadata(apiBaseUrl, signal) {
93305
93404
  const normalizedBaseUrl = trimTrailingSlash(apiBaseUrl);
93306
93405
  const fallback = {
93307
93406
  authorizationEndpoint: `${normalizedBaseUrl}/oauth2/authorize`,
@@ -93311,7 +93410,7 @@ async function discoverOAuthMetadata(apiBaseUrl) {
93311
93410
  };
93312
93411
  const metadataUrl = `${normalizedBaseUrl}/.well-known/oauth-authorization-server`;
93313
93412
  try {
93314
- const metadata = await fetchJson(metadataUrl);
93413
+ const metadata = await fetchJson(metadataUrl, { signal });
93315
93414
  return {
93316
93415
  authorizationEndpoint: metadata.authorization_endpoint ?? fallback.authorizationEndpoint,
93317
93416
  tokenEndpoint: metadata.token_endpoint ?? fallback.tokenEndpoint,
@@ -93399,13 +93498,18 @@ async function refreshOAuthToken(input) {
93399
93498
  client_id: input.clientId,
93400
93499
  refresh_token: input.refreshToken
93401
93500
  });
93402
- return fetchJson(input.tokenEndpoint, {
93403
- method: "POST",
93404
- headers: {
93405
- "Content-Type": "application/x-www-form-urlencoded"
93501
+ return fetchJson(
93502
+ input.tokenEndpoint,
93503
+ {
93504
+ method: "POST",
93505
+ signal: input.signal,
93506
+ headers: {
93507
+ "Content-Type": "application/x-www-form-urlencoded"
93508
+ },
93509
+ body
93406
93510
  },
93407
- body
93408
- }, parseOAuthTokenResponse);
93511
+ parseOAuthTokenResponse
93512
+ );
93409
93513
  }
93410
93514
  function tokenExpiresAt(now, expiresInSeconds, fallbackMs) {
93411
93515
  return now + (expiresInSeconds === void 0 ? fallbackMs : expiresInSeconds * 1e3);
@@ -93580,10 +93684,11 @@ async function listenOnCallbackPort$1(server) {
93580
93684
  const startOAuthFlow = gen(function* () {
93581
93685
  const config = yield* ConfigService;
93582
93686
  const credentials = yield* CredentialService;
93583
- const apiBaseUrl = yield* try_({
93584
- try: () => requireApiBaseUrl(config.load()),
93585
- catch: (error) => toError$1(error, "App URL validation failed")
93687
+ const session = yield* tryPromise({
93688
+ try: () => credentials.withLock(() => captureAuthSession(config, credentials)),
93689
+ catch: (error) => toError$1(error, "Could not read authentication state")
93586
93690
  });
93691
+ const apiBaseUrl = deriveInternalBaseUrls(session.appBaseUrl).apiBaseUrl;
93587
93692
  const pkce = generatePkcePair();
93588
93693
  const state = generateState$1();
93589
93694
  const deferred = yield* make$m();
@@ -93640,12 +93745,15 @@ const startOAuthFlow = gen(function* () {
93640
93745
  console.log("Authorization received. Exchanging token...");
93641
93746
  });
93642
93747
  const tokenResponse = yield* tryPromise({
93643
- try: () => exchangeAuthorizationCode({
93644
- tokenEndpoint: metadata.tokenEndpoint,
93645
- clientId: registration.clientId,
93646
- code: callback.code,
93647
- codeVerifier: pkce.verifier
93648
- }),
93748
+ try: () => {
93749
+ assertAuthSession(session, config, credentials);
93750
+ return exchangeAuthorizationCode({
93751
+ tokenEndpoint: metadata.tokenEndpoint,
93752
+ clientId: registration.clientId,
93753
+ code: callback.code,
93754
+ codeVerifier: pkce.verifier
93755
+ });
93756
+ },
93649
93757
  catch: (error) => toError$1(error, "OAuth token exchange failed")
93650
93758
  });
93651
93759
  const nextCredentials = toCredentialsFromTokenResponse({
@@ -93653,7 +93761,13 @@ const startOAuthFlow = gen(function* () {
93653
93761
  clientId: registration.clientId,
93654
93762
  issuer: metadata.issuer
93655
93763
  });
93656
- credentials.save(nextCredentials);
93764
+ yield* tryPromise({
93765
+ try: () => credentials.withLock(() => {
93766
+ assertAuthSession(session, config, credentials);
93767
+ credentials.save(nextCredentials);
93768
+ }),
93769
+ catch: (error) => toError$1(error, "Could not save authentication state")
93770
+ });
93657
93771
  return nextCredentials;
93658
93772
  }).pipe(ensuring$1(promise$1(() => closeServer())));
93659
93773
  });
@@ -93665,7 +93779,7 @@ const injectedBuildEnv = "dev".length > 0 ? "dev" : "local";
93665
93779
  if (!isBuildEnv(injectedBuildEnv)) {
93666
93780
  throw new Error(`Invalid AIPCTL_BUILD_ENV: ${injectedBuildEnv}`);
93667
93781
  }
93668
- const VERSION = "2026.10.4".length > 0 ? "2026.10.4" : "0.0.0";
93782
+ const VERSION = "2026.10.5".length > 0 ? "2026.10.5" : "0.0.0";
93669
93783
  const BUILD_ENV = injectedBuildEnv;
93670
93784
  const INCLUDE_NATIVE_LOGS = BUILD_ENV !== "prod";
93671
93785
 
@@ -94617,7 +94731,7 @@ const JSON_CONTENT_TYPE = "application/json";
94617
94731
  const AGENT_CHAT_STREAM_RETRY_DELAY_MS = 1e3;
94618
94732
  const AGENT_CHAT_STREAM_MAX_ATTEMPTS = 4;
94619
94733
  function bearerHeaders(accessToken) {
94620
- return { Authorization: `Bearer ${accessToken}` };
94734
+ return { Authorization: `Bearer ${accessToken.value}` };
94621
94735
  }
94622
94736
  function jsonBearerHeaders(accessToken) {
94623
94737
  return {
@@ -94635,6 +94749,13 @@ function apiErrorFromUnknown(error, fallbackMessage) {
94635
94749
  if (error instanceof ApiError) {
94636
94750
  return error;
94637
94751
  }
94752
+ if (error instanceof AuthSessionChangedError) {
94753
+ return createApiError({
94754
+ message: error.message,
94755
+ code: error.code,
94756
+ outcome: "not-dispatched"
94757
+ });
94758
+ }
94638
94759
  if (error instanceof HttpRequestError) {
94639
94760
  return createApiError({
94640
94761
  message: error.message + (error.outcome === "unknown" ? " The server may have completed the action. Check its state before running it again." : ""),
@@ -94833,19 +94954,29 @@ function parseRetryAfterSeconds(value) {
94833
94954
  const makeRequest = (baseUrl, path, accessToken, options) => tryPromise({
94834
94955
  try: async (signal) => {
94835
94956
  const resolvedBaseUrl = typeof baseUrl === "function" ? baseUrl() : baseUrl;
94836
- return withHttpResponse(`${resolvedBaseUrl}${path}`, {
94837
- method: options?.method || "GET",
94838
- headers: jsonBearerHeaders(accessToken),
94839
- body: options?.body ? JSON.stringify(options.body) : void 0
94840
- }, async (response) => {
94841
- if (!response.ok) {
94842
- throw await toApiError(response);
94843
- }
94844
- if (options?.skipJsonParse || response.status === 202 || response.status === 204) {
94845
- return void 0;
94957
+ return withHttpResponse(
94958
+ `${resolvedBaseUrl}${path}`,
94959
+ {
94960
+ method: options?.method || "GET",
94961
+ headers: jsonBearerHeaders(accessToken),
94962
+ body: options?.body ? JSON.stringify(options.body) : void 0
94963
+ },
94964
+ async (response) => {
94965
+ if (!response.ok) {
94966
+ throw await toApiError(response);
94967
+ }
94968
+ if (options?.skipJsonParse || response.status === 202 || response.status === 204) {
94969
+ return void 0;
94970
+ }
94971
+ return await response.json();
94972
+ },
94973
+ {
94974
+ signal,
94975
+ beforeDispatch: () => accessToken.assertCurrent(resolvedBaseUrl),
94976
+ ...options?.timeoutMs ? { timeoutMs: options.timeoutMs } : {},
94977
+ ...options?.responseBudget ? { responseBudget: options.responseBudget } : {}
94846
94978
  }
94847
- return await response.json();
94848
- }, { signal, ...options?.timeoutMs ? { timeoutMs: options.timeoutMs } : {}, ...options?.responseBudget ? { responseBudget: options.responseBudget } : {} });
94979
+ );
94849
94980
  },
94850
94981
  catch: (error) => apiErrorFromUnknown(error, "The request could not be completed.")
94851
94982
  });
@@ -94994,6 +95125,12 @@ const makeApiServiceLive = (buildEnv) => effect$1(
94994
95125
  if (error instanceof ApiError || error instanceof NotLoggedInError) {
94995
95126
  return error;
94996
95127
  }
95128
+ if (error instanceof AuthSessionChangedError) {
95129
+ return createApiError({ message: error.message, code: error.code });
95130
+ }
95131
+ if (error instanceof RefreshLockWaitError) {
95132
+ return createApiError({ message: error.message, code: error.code, outcome: "not-dispatched" });
95133
+ }
94997
95134
  if (error instanceof OAuthRequestError) {
94998
95135
  if (error.statusCode === 401 || error.code === "invalid_grant") {
94999
95136
  return sessionExpired();
@@ -95010,18 +95147,20 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95010
95147
  message: error instanceof Error ? error.message : "OAuth token refresh failed"
95011
95148
  });
95012
95149
  };
95013
- const performOAuthRefresh = async (lockedCreds) => {
95150
+ const performOAuthRefresh = async (lockedCreds, session, signal) => {
95014
95151
  const clientId = lockedCreds.clientId;
95015
95152
  const refreshToken = lockedCreds.refreshToken;
95016
95153
  if (!clientId || !refreshToken) {
95017
- throw clearInvalidSession();
95154
+ throw sessionExpired();
95018
95155
  }
95019
- const metadata = await discoverOAuthMetadata(loadConfiguredApiBaseUrl());
95156
+ const metadata = await discoverOAuthMetadata(deriveInternalBaseUrls(session.appBaseUrl).apiBaseUrl, signal);
95157
+ assertAuthSession(session, configService, credentialService);
95020
95158
  try {
95021
95159
  const tokenResponse = await refreshOAuthToken({
95022
95160
  tokenEndpoint: metadata.tokenEndpoint,
95023
95161
  clientId,
95024
- refreshToken
95162
+ refreshToken,
95163
+ signal
95025
95164
  });
95026
95165
  const nextCredentials = toCredentialsFromTokenResponse({
95027
95166
  tokenResponse,
@@ -95030,11 +95169,17 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95030
95169
  previousRefreshToken: refreshToken,
95031
95170
  previousRefreshExpiresAt: lockedCreds.refreshExpiresAt
95032
95171
  });
95033
- credentialService.save(nextCredentials);
95172
+ await credentialService.withLock(() => {
95173
+ signal.throwIfAborted();
95174
+ assertAuthSession(session, configService, credentialService);
95175
+ credentialService.save(nextCredentials, { preserveSession: true });
95176
+ });
95034
95177
  return nextCredentials;
95035
95178
  } catch (error) {
95036
95179
  if (error instanceof OAuthRequestError && (error.statusCode === 401 || error.code === "invalid_grant")) {
95037
- credentialService.clear();
95180
+ await credentialService.withLock(() => {
95181
+ if (isCurrentAuthSession(session, configService, credentialService)) credentialService.clear();
95182
+ });
95038
95183
  }
95039
95184
  throw error;
95040
95185
  }
@@ -95042,28 +95187,57 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95042
95187
  const refreshAccessToken = (options) => gen(function* () {
95043
95188
  yield* requireApiBaseUrlEffect();
95044
95189
  return yield* tryPromise({
95045
- try: async () => credentialService.withLock(async () => {
95046
- const lockedCreds = credentialService.load();
95047
- if (!lockedCreds) {
95048
- throw notLoggedIn();
95049
- }
95050
- const status = getCredentialAuthStatus(lockedCreds, Date.now(), {
95051
- accessExpiryBufferMs: REFRESH_BUFFER_MS,
95052
- forceRefresh: options?.force
95053
- });
95054
- switch (status) {
95055
- case "authenticated":
95056
- return lockedCreds;
95057
- case "refreshable":
95058
- return performOAuthRefresh(lockedCreds);
95059
- case "missing":
95060
- case "expired":
95061
- throw clearInvalidSession();
95062
- }
95063
- }),
95190
+ try: async (signal) => credentialService.withRefreshLock(
95191
+ async () => {
95192
+ const state = await credentialService.withLock(() => {
95193
+ signal.throwIfAborted();
95194
+ const session = captureAuthSession(configService, credentialService);
95195
+ const lockedCreds = credentialService.load();
95196
+ if (!lockedCreds) {
95197
+ throw notLoggedIn();
95198
+ }
95199
+ const status = getCredentialAuthStatus(lockedCreds, Date.now(), {
95200
+ accessExpiryBufferMs: REFRESH_BUFFER_MS,
95201
+ forceRefresh: options?.force
95202
+ });
95203
+ switch (status) {
95204
+ case "authenticated":
95205
+ case "refreshable":
95206
+ return { lockedCreds, session, status };
95207
+ case "missing":
95208
+ case "expired":
95209
+ throw clearInvalidSession();
95210
+ }
95211
+ });
95212
+ return state.status === "authenticated" ? state.lockedCreds : performOAuthRefresh(state.lockedCreds, state.session, signal);
95213
+ },
95214
+ { signal }
95215
+ ),
95064
95216
  catch: toRefreshFailure
95065
95217
  });
95066
95218
  });
95219
+ const bindAccessToken = (creds) => {
95220
+ const session = captureAuthSession(configService, credentialService);
95221
+ const urls = deriveInternalBaseUrls(session.appBaseUrl);
95222
+ const destinations = [urls.apiBaseUrl, urls.chatBaseUrl].map((url) => new URL(url).origin);
95223
+ return {
95224
+ value: creds.accessToken,
95225
+ assertCurrent: (destination) => {
95226
+ assertAuthSession(session, configService, credentialService);
95227
+ if (!destinations.includes(new URL(destination).origin)) {
95228
+ throw new AuthSessionChangedError({
95229
+ code: "CLI_AUTH_SESSION_CHANGED",
95230
+ message: 'App URL changed. Check "aipctl config status" before trying again.'
95231
+ });
95232
+ }
95233
+ if (credentialService.load()?.accessToken !== creds.accessToken) throw notLoggedIn();
95234
+ }
95235
+ };
95236
+ };
95237
+ const bindAccessTokenEffect = (creds) => try_({
95238
+ try: () => bindAccessToken(creds),
95239
+ catch: toRefreshFailure
95240
+ });
95067
95241
  const getAccessToken = () => gen(function* () {
95068
95242
  yield* requireApiBaseUrlEffect();
95069
95243
  const creds = credentialService.load();
@@ -95075,14 +95249,14 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95075
95249
  });
95076
95250
  switch (status) {
95077
95251
  case "authenticated":
95078
- return creds.accessToken;
95252
+ return yield* bindAccessTokenEffect(creds);
95079
95253
  case "refreshable": {
95080
95254
  const refreshedCredentials = yield* refreshAccessToken();
95081
- return refreshedCredentials.accessToken;
95255
+ return yield* bindAccessTokenEffect(refreshedCredentials);
95082
95256
  }
95083
95257
  case "missing":
95084
95258
  case "expired":
95085
- return yield* fail$3(clearInvalidSession());
95259
+ return yield* refreshAccessToken().pipe(flatMap$5(bindAccessTokenEffect));
95086
95260
  }
95087
95261
  });
95088
95262
  const baseUrl = () => loadConfiguredApiBaseUrl();
@@ -95090,11 +95264,13 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95090
95264
  const requireReleaseEnabledOperation = (operationId) => {
95091
95265
  if (buildEnv !== "prod") return _void;
95092
95266
  if (!releaseEnabledCommandOperationIds.has(operationId)) {
95093
- return fail$3(createApiError({
95094
- message: `CLI operation "${operationId}" is not enabled in this aipctl release.`,
95095
- statusCode: 404,
95096
- code: "CLI_OPERATION_NOT_ENABLED"
95097
- }));
95267
+ return fail$3(
95268
+ createApiError({
95269
+ message: `CLI operation "${operationId}" is not enabled in this aipctl release.`,
95270
+ statusCode: 404,
95271
+ code: "CLI_OPERATION_NOT_ENABLED"
95272
+ })
95273
+ );
95098
95274
  }
95099
95275
  return _void;
95100
95276
  };
@@ -95106,19 +95282,30 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95106
95282
  const deadline = Date.now() + TRANSFER_DEADLINE_MS;
95107
95283
  for (let attempt = 1; attempt <= AGENT_CHAT_STREAM_MAX_ATTEMPTS; attempt += 1) {
95108
95284
  try {
95109
- return await withHttpResponse(`${resolvedChatBaseUrl}${path}`, {
95110
- method: "POST",
95111
- headers: jsonBearerHeaders(token),
95112
- body: JSON.stringify({})
95113
- }, async (response) => {
95114
- if (!response.ok) {
95115
- throw await toApiError(response);
95116
- }
95117
- if (!response.body) {
95118
- throw createApiError({ message: "No response body from agent chat stream endpoint" });
95285
+ return await withHttpResponse(
95286
+ `${resolvedChatBaseUrl}${path}`,
95287
+ {
95288
+ method: "POST",
95289
+ headers: jsonBearerHeaders(token),
95290
+ body: JSON.stringify({})
95291
+ },
95292
+ async (response) => {
95293
+ if (!response.ok) {
95294
+ throw await toApiError(response);
95295
+ }
95296
+ if (!response.body) {
95297
+ throw createApiError({
95298
+ message: "No response body from agent chat stream endpoint"
95299
+ });
95300
+ }
95301
+ return await parseSseStream(response.body);
95302
+ },
95303
+ {
95304
+ signal,
95305
+ beforeDispatch: () => token.assertCurrent(resolvedChatBaseUrl),
95306
+ timeoutMs: Math.max(1, deadline - Date.now())
95119
95307
  }
95120
- return await parseSseStream(response.body);
95121
- }, { signal, timeoutMs: Math.max(1, deadline - Date.now()) });
95308
+ );
95122
95309
  } catch (error) {
95123
95310
  if (error instanceof AgentChatStreamIncompleteError && error.sawOnlyTerminalEnvelope && attempt < AGENT_CHAT_STREAM_MAX_ATTEMPTS) {
95124
95311
  await sleep(AGENT_CHAT_STREAM_RETRY_DELAY_MS);
@@ -95158,10 +95345,12 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95158
95345
  token
95159
95346
  );
95160
95347
  if (!conversation?.agentInstallationId) {
95161
- return yield* fail$3(createApiError({
95162
- message: `Conversation "${conversationId}" is missing an agent installation id.`,
95163
- statusCode: 400
95164
- }));
95348
+ return yield* fail$3(
95349
+ createApiError({
95350
+ message: `Conversation "${conversationId}" is missing an agent installation id.`,
95351
+ statusCode: 400
95352
+ })
95353
+ );
95165
95354
  }
95166
95355
  return conversation.agentInstallationId;
95167
95356
  });
@@ -95647,10 +95836,14 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95647
95836
  return { ...response, pagination: normalizePageNumberPagination(response.pagination, response.list.length) };
95648
95837
  }),
95649
95838
  // Phase 5: Drive/Integration
95650
- listDriveEntries: (orgSlug) => gen(function* () {
95839
+ listDriveEntries: (orgSlug, options) => gen(function* () {
95651
95840
  const token = yield* getAccessToken();
95652
95841
  yield* requireReleaseEnabledOperation("listCliDriveEntries");
95653
- const params = new URLSearchParams({ count: "200" });
95842
+ const params = new URLSearchParams({
95843
+ count: String(options?.count ?? 200)
95844
+ });
95845
+ if (options?.cursor !== void 0) params.set("cursor", options.cursor);
95846
+ if (options?.parentId !== void 0) params.set("parentId", options.parentId);
95654
95847
  const path = `/api/cli/v1/organizations/${encodeURIComponent(orgSlug)}/drive/entries?${params.toString()}`;
95655
95848
  return yield* makeRequest(baseUrl, path, token);
95656
95849
  }),
@@ -95792,15 +95985,25 @@ const makeApiServiceLive = (buildEnv) => effect$1(
95792
95985
  const token = yield* getAccessToken();
95793
95986
  const path = `/web/admin/organizations/slug/${encodeURIComponent(orgSlug)}/installed-agents/${encodeURIComponent(agentId)}/export`;
95794
95987
  return yield* tryPromise({
95795
- try: async (signal) => withHttpResponse(`${resolvedApiBaseUrl}${path}`, {
95796
- headers: bearerHeaders(token)
95797
- }, async (response) => {
95798
- if (!response.ok) {
95799
- throw await toApiError(response);
95988
+ try: async (signal) => withHttpResponse(
95989
+ `${resolvedApiBaseUrl}${path}`,
95990
+ {
95991
+ headers: bearerHeaders(token)
95992
+ },
95993
+ async (response) => {
95994
+ if (!response.ok) {
95995
+ throw await toApiError(response);
95996
+ }
95997
+ const arrayBuffer = await response.arrayBuffer();
95998
+ return Buffer.from(arrayBuffer);
95999
+ },
96000
+ {
96001
+ signal,
96002
+ beforeDispatch: () => token.assertCurrent(resolvedApiBaseUrl),
96003
+ timeoutMs: TRANSFER_DEADLINE_MS,
96004
+ maxResponseBytes: TRANSFER_MAX_RESPONSE_BYTES
95800
96005
  }
95801
- const arrayBuffer = await response.arrayBuffer();
95802
- return Buffer.from(arrayBuffer);
95803
- }, { signal, timeoutMs: TRANSFER_DEADLINE_MS, maxResponseBytes: TRANSFER_MAX_RESPONSE_BYTES }),
96006
+ ),
95804
96007
  catch: (error) => apiErrorFromUnknown(error, "Export failed")
95805
96008
  });
95806
96009
  }),
@@ -96119,7 +96322,9 @@ const ApiService = ApiServiceTag;
96119
96322
  function jsonOutputRequested(args) {
96120
96323
  const forced = args.indexOf("--");
96121
96324
  const options = forced === -1 ? args : args.slice(0, forced);
96122
- return options.some((arg, index) => arg === "--json=true" || arg === "--json" && options[index + 1] !== "false");
96325
+ return options.some(
96326
+ (arg, index) => ["--json=true", "--json-page=true"].includes(arg) || ["--json", "--json-page"].includes(arg) && options[index + 1] !== "false"
96327
+ );
96123
96328
  }
96124
96329
  function reportInterruption(json) {
96125
96330
  const message = "The command was interrupted. Check the status of any change or execution before running it again.";
@@ -96170,9 +96375,21 @@ function withCliFailureOutput(program, json) {
96170
96375
  });
96171
96376
  }
96172
96377
  function formatCliFailure(error, json) {
96173
- if (!(error instanceof ApiError || error instanceof OAuthRequestError || error instanceof NotLoggedInError || error instanceof NoOrganizationSelectedError)) return void 0;
96378
+ if (error instanceof AuthSessionChangedError)
96379
+ return json ? JSON.stringify({
96380
+ error: {
96381
+ code: error.code,
96382
+ message: error.message,
96383
+ outcome: "not-dispatched"
96384
+ }
96385
+ }) : error.message;
96386
+ if (!(error instanceof ApiError || error instanceof OAuthRequestError || error instanceof NotLoggedInError || error instanceof NoOrganizationSelectedError))
96387
+ return void 0;
96174
96388
  const diagnosticError = error instanceof ApiError || error instanceof OAuthRequestError;
96175
- if (!json) return maskSensitiveText(error.message + (diagnosticError && error.requestId ? ` Request ID: ${error.requestId}` : ""));
96389
+ if (!json)
96390
+ return maskSensitiveText(
96391
+ error.message + (diagnosticError && error.requestId ? ` Request ID: ${error.requestId}` : "")
96392
+ );
96176
96393
  return JSON.stringify({ error: {
96177
96394
  code: diagnosticError ? error.code ?? (error instanceof OAuthRequestError ? "CLI_OAUTH_ERROR" : "CLI_API_ERROR") : error._tag === "NotLoggedInError" ? "CLI_AUTH_REQUIRED" : "CLI_ORGANIZATION_REQUIRED",
96178
96395
  message: maskSensitiveText(error.message),
@@ -96244,21 +96461,101 @@ const isNativeCommandEnabled = (command, includeLogs = INCLUDE_NATIVE_LOGS) => {
96244
96461
  const FORCED_ARGS_SEPARATOR = "--";
96245
96462
  const NATIVE_ROOT_COMMANDS = new Set(nativeCommandNames);
96246
96463
  const trailingOptionSpecs = [
96464
+ ...[
96465
+ ["skill", "library"],
96466
+ ["skill", "built-in"],
96467
+ ["admin", "skill", "built-in"],
96468
+ ["admin", "skill", "list"]
96469
+ ].map(
96470
+ (command) => ({
96471
+ command,
96472
+ booleanOptions: new Set(command.at(-1) === "list" ? ["--json", "--json-page"] : ["--json-page"]),
96473
+ valueOptions: /* @__PURE__ */ new Set([
96474
+ "--org",
96475
+ "--search",
96476
+ ...command.at(-1) === "list" ? ["--page", "--page-size"] : ["--cursor", command.at(-1) === "library" ? "--count" : "--limit"]
96477
+ ]),
96478
+ explicitBooleanValues: true
96479
+ })
96480
+ ),
96481
+ ...["skill", "chat", "drive"].map(
96482
+ (domain) => ({
96483
+ command: [domain, "list"],
96484
+ booleanOptions: new Set(domain === "drive" ? ["--json-page"] : ["--json", "--json-page"]),
96485
+ valueOptions: /* @__PURE__ */ new Set([
96486
+ "--org",
96487
+ "--cursor",
96488
+ ...domain === "chat" ? [] : ["--count"],
96489
+ ...domain === "skill" ? ["--search"] : domain === "drive" ? ["--parent-id"] : []
96490
+ ]),
96491
+ explicitBooleanValues: true
96492
+ })
96493
+ ),
96494
+ ...[
96495
+ ["skill", "delete"],
96496
+ ["admin", "skill", "delete"],
96497
+ ["admin", "integration", "delete"]
96498
+ ].map(
96499
+ (command) => ({
96500
+ command,
96501
+ booleanOptions: /* @__PURE__ */ new Set(["--force", "-f"]),
96502
+ valueOptions: /* @__PURE__ */ new Set(["--org"]),
96503
+ explicitBooleanValues: true,
96504
+ booleanAliases: { "-f": "--force" }
96505
+ })
96506
+ ),
96247
96507
  { command: ["quota", "status"], booleanOptions: /* @__PURE__ */ new Set(["--json"]), valueOptions: /* @__PURE__ */ new Set(["--org"]), explicitBooleanValues: true },
96248
- ...["list", "get", "create", "update", "rename", "delete", "set-default", "unset-default"].map((command) => ({
96249
- command: ["admin", "preset", command],
96250
- booleanOptions: new Set(command === "delete" ? ["--force", "-f"] : ["list", "get"].includes(command) ? ["--json"] : []),
96251
- valueOptions: /* @__PURE__ */ new Set(["--org", ...command === "list" ? ["--page", "--pageSize"] : []]),
96252
- explicitBooleanValues: true,
96253
- booleanAliases: { "-f": "--force" }
96254
- })),
96255
- ...["invite", "remove-member", "update-member-role"].map((command) => ({
96256
- command: ["admin", "org", command],
96257
- booleanOptions: new Set(command === "remove-member" ? ["--force", "-f"] : []),
96258
- valueOptions: /* @__PURE__ */ new Set(["--org", ...command !== "remove-member" ? ["--role"] : []]),
96259
- explicitBooleanValues: true,
96260
- booleanAliases: { "-f": "--force" }
96261
- })),
96508
+ ...["list", "get", "enable", "disable"].map(
96509
+ (command) => ({
96510
+ command: ["agent", command],
96511
+ booleanOptions: new Set(["list", "get"].includes(command) ? ["--json"] : []),
96512
+ valueOptions: /* @__PURE__ */ new Set(["--org"]),
96513
+ explicitBooleanValues: true
96514
+ })
96515
+ ),
96516
+ ...["create", "update", "delete", "share", "templates"].map(
96517
+ (command) => ({
96518
+ command: ["admin", "agent", command],
96519
+ booleanOptions: new Set(command === "delete" ? ["--force", "-f"] : []),
96520
+ valueOptions: /* @__PURE__ */ new Set(["--org"]),
96521
+ explicitBooleanValues: true,
96522
+ booleanAliases: { "-f": "--force" }
96523
+ })
96524
+ ),
96525
+ ...["mkdir", "rename"].map(
96526
+ (command) => ({
96527
+ command: ["drive", command],
96528
+ booleanOptions: /* @__PURE__ */ new Set(),
96529
+ valueOptions: /* @__PURE__ */ new Set(["--org"])
96530
+ })
96531
+ ),
96532
+ ...["create", "update", "enable", "disable"].map(
96533
+ (command) => ({
96534
+ command: ["automation", command],
96535
+ booleanOptions: /* @__PURE__ */ new Set(),
96536
+ valueOptions: /* @__PURE__ */ new Set(["--org"])
96537
+ })
96538
+ ),
96539
+ ...["list", "get", "create", "update", "rename", "delete", "set-default", "unset-default"].map(
96540
+ (command) => ({
96541
+ command: ["admin", "preset", command],
96542
+ booleanOptions: new Set(
96543
+ command === "delete" ? ["--force", "-f"] : ["list", "get"].includes(command) ? ["--json"] : []
96544
+ ),
96545
+ valueOptions: /* @__PURE__ */ new Set(["--org", ...command === "list" ? ["--page", "--pageSize"] : []]),
96546
+ explicitBooleanValues: true,
96547
+ booleanAliases: { "-f": "--force" }
96548
+ })
96549
+ ),
96550
+ ...["invite", "remove-member", "update-member-role"].map(
96551
+ (command) => ({
96552
+ command: ["admin", "org", command],
96553
+ booleanOptions: new Set(command === "remove-member" ? ["--force", "-f"] : []),
96554
+ valueOptions: /* @__PURE__ */ new Set(["--org", ...command !== "remove-member" ? ["--role"] : []]),
96555
+ explicitBooleanValues: true,
96556
+ booleanAliases: { "-f": "--force" }
96557
+ })
96558
+ ),
96262
96559
  {
96263
96560
  command: ["org", "exit"],
96264
96561
  booleanOptions: /* @__PURE__ */ new Set(["--force", "-f"]),
@@ -96266,12 +96563,14 @@ const trailingOptionSpecs = [
96266
96563
  explicitBooleanValues: true,
96267
96564
  booleanAliases: { "-f": "--force" }
96268
96565
  },
96269
- ...["list", "get"].map((command) => ({
96270
- command: ["automation", command],
96271
- booleanOptions: /* @__PURE__ */ new Set(["--json"]),
96272
- valueOptions: /* @__PURE__ */ new Set(["--org", ...command === "list" ? ["--name"] : []]),
96273
- explicitBooleanValues: true
96274
- })),
96566
+ ...["list", "get"].map(
96567
+ (command) => ({
96568
+ command: ["automation", command],
96569
+ booleanOptions: /* @__PURE__ */ new Set(["--json"]),
96570
+ valueOptions: /* @__PURE__ */ new Set(["--org", ...command === "list" ? ["--name"] : []]),
96571
+ explicitBooleanValues: true
96572
+ })
96573
+ ),
96275
96574
  {
96276
96575
  command: ["automation", "delete"],
96277
96576
  booleanOptions: /* @__PURE__ */ new Set(["--force", "-f"]),
@@ -96282,7 +96581,9 @@ const trailingOptionSpecs = [
96282
96581
  {
96283
96582
  command: ["drive", "delete"],
96284
96583
  booleanOptions: /* @__PURE__ */ new Set(["--force", "-f"]),
96285
- valueOptions: /* @__PURE__ */ new Set(["--org"])
96584
+ valueOptions: /* @__PURE__ */ new Set(["--org"]),
96585
+ explicitBooleanValues: true,
96586
+ booleanAliases: { "-f": "--force" }
96286
96587
  },
96287
96588
  {
96288
96589
  command: ["automation", "history"],
@@ -96331,8 +96632,11 @@ const normalizeTrailingOptions = (args, spec) => {
96331
96632
  continue;
96332
96633
  }
96333
96634
  if (isValueOption(arg, spec)) {
96334
- options.push(arg);
96335
96635
  const nextArg = args[index + 1];
96636
+ if (!arg.includes("=") && (nextArg === void 0 || nextArg.startsWith("-"))) {
96637
+ return [...args];
96638
+ }
96639
+ options.push(arg);
96336
96640
  if (!arg.includes("=") && nextArg !== void 0 && !nextArg.startsWith("-")) {
96337
96641
  index += 1;
96338
96642
  options.push(nextArg);
@@ -96483,22 +96787,26 @@ const createCommand$2 = make$4(
96483
96787
  })
96484
96788
  ).pipe(withDescription("Create an installed agent from a JSON file"));
96485
96789
 
96790
+ const requireConfirmation = (force, action) => force ? _void : fail$3(
96791
+ new ApiError({
96792
+ code: "CLI_CONFIRMATION_REQUIRED",
96793
+ outcome: "not-dispatched",
96794
+ message: `${action} was not performed. Use --force (-f) to confirm.`
96795
+ })
96796
+ );
96797
+
96486
96798
  const agentIdArg$5 = text$2({ name: "id" }).pipe(withDescription$5("Agent ID to delete"));
96487
96799
  const forceOption$8 = boolean("force").pipe(
96488
96800
  withAlias("f"),
96489
- withDescription$3("Skip confirmation prompt")
96801
+ withDescription$3("Confirm permanent deletion")
96490
96802
  );
96491
96803
  const deleteCommand$6 = make$4(
96492
96804
  "delete",
96493
96805
  { agentId: agentIdArg$5, org: orgOption, force: forceOption$8 },
96494
96806
  ({ agentId, org, force }) => gen(function* () {
96807
+ yield* requireConfirmation(force, "Agent deletion");
96495
96808
  const api = yield* ApiService;
96496
96809
  const orgSlug = yield* resolveOrgSlug(org, api);
96497
- if (!force) {
96498
- yield* log(`Warning: This will permanently delete agent "${agentId}".`);
96499
- yield* log("Use --force to skip this confirmation.");
96500
- return;
96501
- }
96502
96810
  yield* log(`Deleting agent ${agentId}...`);
96503
96811
  yield* api.deleteInstalledAgent(orgSlug, agentId).pipe(
96504
96812
  catchTag(
@@ -96887,19 +97195,15 @@ const availableCommand = make$4(
96887
97195
  const integrationIdArg$4 = text$2({ name: "id" }).pipe(withDescription$5("Integration ID to delete"));
96888
97196
  const forceOption$7 = boolean("force").pipe(
96889
97197
  withAlias("f"),
96890
- withDescription$3("Skip confirmation prompt")
97198
+ withDescription$3("Confirm permanent deletion")
96891
97199
  );
96892
97200
  const deleteCommand$5 = make$4(
96893
97201
  "delete",
96894
97202
  { integrationId: integrationIdArg$4, org: orgOption, force: forceOption$7 },
96895
97203
  ({ integrationId, org, force }) => gen(function* () {
97204
+ yield* requireConfirmation(force, "Integration deletion");
96896
97205
  const api = yield* ApiService;
96897
97206
  const orgSlug = yield* resolveOrgSlug(org, api);
96898
- if (!force) {
96899
- yield* log(`Warning: This will permanently delete integration "${integrationId}".`);
96900
- yield* log("Use --force to skip this confirmation.");
96901
- return;
96902
- }
96903
97207
  yield* log(`Deleting integration ${integrationId}...`);
96904
97208
  yield* api.deleteIntegration(orgSlug, integrationId).pipe(
96905
97209
  catchTag(
@@ -97158,13 +97462,14 @@ const emailsArg$1 = text$2({ name: "email" }).pipe(
97158
97462
  );
97159
97463
  const forceOption$6 = boolean("force").pipe(
97160
97464
  withAlias("f"),
97161
- withDescription$3("Skip confirmation prompt"),
97465
+ withDescription$3("Confirm member removal"),
97162
97466
  withDefault(false)
97163
97467
  );
97164
97468
  const removeMemberCommand = make$4(
97165
97469
  "remove-member",
97166
97470
  { emails: emailsArg$1, org: orgOption, force: forceOption$6 },
97167
97471
  ({ emails, org, force }) => gen(function* () {
97472
+ yield* requireConfirmation(force, "Member removal");
97168
97473
  const api = yield* ApiService;
97169
97474
  const orgSlug = yield* resolveOrgSlug(org, api);
97170
97475
  const emailList = Array.from(emails);
@@ -97172,11 +97477,6 @@ const removeMemberCommand = make$4(
97172
97477
  yield* error$1("Please provide at least one email address to remove.");
97173
97478
  return yield* fail$3(new Error("No email addresses provided"));
97174
97479
  }
97175
- if (!force) {
97176
- yield* log(`Warning: This will remove ${emailList.join(", ")} from the organization.`);
97177
- yield* log("Use --force (-f) to confirm.");
97178
- return;
97179
- }
97180
97480
  const membersResult = yield* api.listOrgMembers(orgSlug).pipe(
97181
97481
  catchTag(
97182
97482
  "NotLoggedInError",
@@ -97342,7 +97642,7 @@ const createCommand$1 = make$4(
97342
97642
 
97343
97643
  const idArg$7 = text$2({ name: "id" }).pipe(withDescription$5("Preset ID"));
97344
97644
  const forceOption$5 = boolean("force").pipe(
97345
- withDescription$3("Skip confirmation prompt"),
97645
+ withDescription$3("Confirm permanent deletion"),
97346
97646
  withAlias("f"),
97347
97647
  withDefault(false)
97348
97648
  );
@@ -97350,13 +97650,9 @@ const deleteCommand$4 = make$4(
97350
97650
  "delete",
97351
97651
  { id: idArg$7, org: orgOption, force: forceOption$5 },
97352
97652
  ({ id, org, force }) => gen(function* () {
97653
+ yield* requireConfirmation(force, "Preset deletion");
97353
97654
  const api = yield* ApiService;
97354
97655
  const orgSlug = yield* resolveOrgSlug(org, api);
97355
- if (!force) {
97356
- yield* log(`Warning: This will permanently delete preset "${id}".`);
97357
- yield* log("Use --force (-f) to skip this confirmation.");
97358
- return;
97359
- }
97360
97656
  yield* api.deletePreset(id, orgSlug).pipe(
97361
97657
  catchTag(
97362
97658
  "NotLoggedInError",
@@ -97695,18 +97991,62 @@ const presetCommand = make$4("preset", {}).pipe(
97695
97991
  withSubcommands(presetSubcommands)
97696
97992
  );
97697
97993
 
97994
+ const cursorOption = text("cursor").pipe(
97995
+ withDescription$3("Continue from the previous page nextCursor"),
97996
+ optional
97997
+ );
97998
+ const countOption = integer("count").pipe(
97999
+ withDescription$3("Items per page (1\u2013200)"),
98000
+ optional
98001
+ );
98002
+ const jsonPageOption = boolean("json-page").pipe(
98003
+ withDescription$3("Output the full page, including pagination metadata"),
98004
+ withDefault(false)
98005
+ );
98006
+ function validatePageOptions(count, json, jsonPage, maximum = 200, option = "--count") {
98007
+ if (count !== void 0 && (count < 1 || count > maximum)) {
98008
+ return fail$3(
98009
+ new ApiError({
98010
+ message: `${option} must be between 1 and ${maximum}.`,
98011
+ code: "CLI_INVALID_ARGUMENT",
98012
+ outcome: "not-dispatched"
98013
+ })
98014
+ );
98015
+ }
98016
+ if (json && jsonPage) {
98017
+ return fail$3(
98018
+ new ApiError({
98019
+ message: "Choose either --json or --json-page.",
98020
+ code: "CLI_INVALID_ARGUMENT",
98021
+ outcome: "not-dispatched"
98022
+ })
98023
+ );
98024
+ }
98025
+ return _void;
98026
+ }
98027
+ const reportNextPage = (hasNext) => hasNext ? error$1(
98028
+ "More results are available. Use --json-page to read nextCursor, then pass it with --cursor using the same filters."
98029
+ ) : _void;
98030
+
98031
+ const limitOption$2 = integer("limit").pipe(withDescription$3("Items per page (1\u2013100)"), optional);
97698
98032
  const searchOption$3 = text("search").pipe(
97699
98033
  withDescription$3("Search built-in skills by name"),
97700
98034
  optional
97701
98035
  );
97702
98036
  const makeBuiltInCommand = (listSkills = (api, orgSlug, options) => api.listBuiltInSkills(orgSlug, options), description = "List built-in skills from the library") => make$4(
97703
98037
  "built-in",
97704
- { org: orgOption, search: searchOption$3 },
97705
- ({ org, search }) => gen(function* () {
98038
+ { org: orgOption, search: searchOption$3, limit: limitOption$2, cursor: cursorOption, jsonPage: jsonPageOption },
98039
+ ({ org, search, limit, cursor, jsonPage }) => gen(function* () {
98040
+ const limitValue = limit._tag === "Some" ? limit.value : void 0;
98041
+ yield* validatePageOptions(limitValue, false, jsonPage, 100, "--limit");
97706
98042
  const api = yield* ApiService;
97707
98043
  const orgSlug = yield* resolveOrgSlug(org, api);
97708
98044
  const searchValue = search._tag === "Some" ? search.value : void 0;
97709
- const result = yield* listSkills(api, orgSlug, { search: searchValue }).pipe(
98045
+ const result = yield* listSkills(api, orgSlug, {
98046
+ search: searchValue,
98047
+ limit: limitValue,
98048
+ cursor: cursor._tag === "Some" ? cursor.value : void 0
98049
+ }).pipe(
97710
98050
  catchTag(
97711
98051
  "NotLoggedInError",
97712
98052
  (e) => gen(function* () {
@@ -97722,6 +98062,11 @@ const makeBuiltInCommand = (listSkills = (api, orgSlug, options) => api.listBuil
97722
98062
  })
97723
98063
  )
97724
98064
  );
98065
+ if (jsonPage) {
98066
+ yield* log(jsonFormat({ ...result, nextCursor: result.nextCursor ?? null }));
98067
+ return;
98068
+ }
98069
+ yield* reportNextPage(result.hasNext);
97725
98070
  if (result.items.length === 0) {
97726
98071
  yield* log("No built-in skills found.");
97727
98072
  return;
@@ -97740,9 +98085,6 @@ const makeBuiltInCommand = (listSkills = (api, orgSlug, options) => api.listBuil
97740
98085
  yield* log(` Updated: ${formatDate(skill.updatedAt)}`);
97741
98086
  yield* log("");
97742
98087
  }
97743
- if (result.hasNext) {
97744
- yield* log("(More built-in skills available.)");
97745
- }
97746
98088
  })
97747
98089
  ).pipe(withDescription(description));
97748
98090
  const builtInCommand = makeBuiltInCommand();
@@ -98118,7 +98460,7 @@ const jsonOption$e = boolean("json").pipe(
98118
98460
  const skillIdArg$2 = text$2({ name: "id" }).pipe(withDescription$5("Skill ID"));
98119
98461
  const forceOption$4 = boolean("force").pipe(
98120
98462
  withAlias("f"),
98121
- withDescription$3("Skip confirmation prompt")
98463
+ withDescription$3("Confirm permanent deletion")
98122
98464
  );
98123
98465
  const fileArg$2 = file({ name: "file", exists: "yes" }).pipe(
98124
98466
  withDescription$5("Path to the skill file (.zip or .skill)")
@@ -98128,8 +98470,16 @@ const overwriteOption$1 = boolean("overwrite").pipe(
98128
98470
  );
98129
98471
  const listCommand$8 = make$4(
98130
98472
  "list",
98131
- { org: orgOption, search: searchOption$2, page: pageOption$2, pageSize: pageSizeOption, json: jsonOption$e },
98132
- ({ org, search, page, pageSize, json }) => gen(function* () {
98473
+ {
98474
+ org: orgOption,
98475
+ search: searchOption$2,
98476
+ page: pageOption$2,
98477
+ pageSize: pageSizeOption,
98478
+ json: jsonOption$e,
98479
+ jsonPage: jsonPageOption
98480
+ },
98481
+ ({ org, search, page, pageSize, json, jsonPage }) => gen(function* () {
98482
+ yield* validatePageOptions(void 0, json, jsonPage);
98133
98483
  const api = yield* ApiService;
98134
98484
  const orgSlug = yield* resolveOrgSlug(org, api);
98135
98485
  const result = yield* api.listAdminSkills(orgSlug, {
@@ -98152,6 +98502,15 @@ const listCommand$8 = make$4(
98152
98502
  })
98153
98503
  )
98154
98504
  );
98505
+ if (jsonPage) {
98506
+ yield* log(jsonFormat(result));
98507
+ return;
98508
+ }
98509
+ if (result.pagination.page * result.pagination.pageSize < result.pagination.total) {
98510
+ yield* error$1(
98511
+ "More results are available. Use --json-page to read pagination, then pass --page using the same filters."
98512
+ );
98513
+ }
98155
98514
  if (json) {
98156
98515
  yield* log(jsonFormat(result.list));
98157
98516
  return;
@@ -98338,13 +98697,9 @@ const deleteCommand$3 = make$4(
98338
98697
  "delete",
98339
98698
  { skillId: skillIdArg$2, org: orgOption, force: forceOption$4 },
98340
98699
  ({ skillId, org, force }) => gen(function* () {
98700
+ yield* requireConfirmation(force, "Organization skill deletion");
98341
98701
  const api = yield* ApiService;
98342
98702
  const orgSlug = yield* resolveOrgSlug(org, api);
98343
- if (!force) {
98344
- yield* log(`Warning: This will permanently delete organization skill "${skillId}".`);
98345
- yield* log("Use --force to skip this confirmation.");
98346
- return;
98347
- }
98348
98703
  yield* api.deleteAdminSkill(orgSlug, skillId).pipe(
98349
98704
  catchTag(
98350
98705
  "ApiError",
@@ -99274,7 +99629,7 @@ function saveOrganization(org, configService) {
99274
99629
  });
99275
99630
  }
99276
99631
  function toSelectionError(error) {
99277
- return new Error(error.message);
99632
+ return error;
99278
99633
  }
99279
99634
  function findOrganizationBySlug(organizations, slug) {
99280
99635
  return organizations?.find((org) => org.slug === slug);
@@ -99286,17 +99641,27 @@ const getOrganizationBySlug = (slug) => gen(function* () {
99286
99641
  catchTag("ApiError", (e) => fail$3(toSelectionError(e)))
99287
99642
  );
99288
99643
  });
99289
- const saveSelectedOrganization = (slug, knownOrganizations) => gen(function* () {
99644
+ const saveSelectedOrganization = (slug, knownOrganizations, initialSession) => gen(function* () {
99290
99645
  const configService = yield* ConfigService;
99646
+ const credentials = yield* CredentialService;
99647
+ const session = initialSession ?? (yield* promise$1(() => credentials.withLock(() => captureAuthSession(configService, credentials))));
99291
99648
  const org = findOrganizationBySlug(knownOrganizations, slug) ?? (yield* getOrganizationBySlug(slug));
99292
99649
  if (org.ipBlocked) {
99293
99650
  return yield* fail$3(new Error(`Organization "${slug}" is blocked due to IP restrictions.`));
99294
99651
  }
99295
- saveOrganization(org, configService);
99652
+ yield* tryPromise({
99653
+ try: () => credentials.withLock(() => {
99654
+ assertAuthSession(session, configService, credentials);
99655
+ saveOrganization(org, configService);
99656
+ }),
99657
+ catch: (error) => toError(error, "Organization selection failed")
99658
+ });
99296
99659
  return org;
99297
99660
  });
99298
- const startOrganizationSelectionBrowserFlow = (knownOrganizations) => gen(function* () {
99661
+ const startOrganizationSelectionBrowserFlow = (knownOrganizations, initialSession) => gen(function* () {
99299
99662
  const config = yield* ConfigService;
99663
+ const credentials = yield* CredentialService;
99664
+ const session = initialSession ?? (yield* promise$1(() => credentials.withLock(() => captureAuthSession(config, credentials))));
99300
99665
  const appBaseUrl = yield* try_({
99301
99666
  try: () => requireAppBaseUrl(config.load()),
99302
99667
  catch: (error) => toError(error, "App URL validation failed")
@@ -99338,11 +99703,14 @@ const startOrganizationSelectionBrowserFlow = (knownOrganizations) => gen(functi
99338
99703
  _await(deferred),
99339
99704
  sleep$1("5 minutes").pipe(flatMap$5(() => fail$3(new Error(ORG_SELECTION_TIMEOUT_MESSAGE))))
99340
99705
  );
99341
- return yield* saveSelectedOrganization(callback.slug, knownOrganizations);
99706
+ return yield* saveSelectedOrganization(callback.slug, knownOrganizations, session);
99342
99707
  }).pipe(ensuring$1(promise$1(() => closeServer())));
99343
99708
  });
99344
99709
  const selectOrganizationAfterLogin = gen(function* () {
99345
99710
  const api = yield* ApiService;
99711
+ const config = yield* ConfigService;
99712
+ const credentials = yield* CredentialService;
99713
+ const session = yield* promise$1(() => credentials.withLock(() => captureAuthSession(config, credentials)));
99346
99714
  yield* log("Selecting organization...");
99347
99715
  const organizations = yield* api.listOrganizations().pipe(
99348
99716
  catchTag("NotLoggedInError", (e) => fail$3(toSelectionError(e))),
@@ -99356,11 +99724,11 @@ const selectOrganizationAfterLogin = gen(function* () {
99356
99724
  if (!org2) {
99357
99725
  return yield* fail$3(new Error(NO_ORGANIZATIONS_LOGIN_MESSAGE));
99358
99726
  }
99359
- const selectedOrg = yield* saveSelectedOrganization(org2.slug, organizations);
99727
+ const selectedOrg = yield* saveSelectedOrganization(org2.slug, organizations, session);
99360
99728
  yield* log(`\u2713 Selected organization: ${selectedOrg.name} (${selectedOrg.slug})`);
99361
99729
  return selectedOrg;
99362
99730
  }
99363
- const org = yield* startOrganizationSelectionBrowserFlow(organizations).pipe(
99731
+ const org = yield* startOrganizationSelectionBrowserFlow(organizations, session).pipe(
99364
99732
  catchAll((error) => {
99365
99733
  if (error.message === ORG_SELECTION_TIMEOUT_MESSAGE) {
99366
99734
  return fail$3(new Error(`${ORG_SELECTION_TIMEOUT_MESSAGE}
@@ -99425,19 +99793,25 @@ const loginCommand = make$4(
99425
99793
  () => loginEffect(startOAuthFlow, selectOrganizationAfterLogin)
99426
99794
  ).pipe(withDescription("Log in to AIP via browser-based OAuth"));
99427
99795
 
99428
- const logoutCommand = make$4(
99429
- "logout",
99430
- {},
99431
- () => gen(function* () {
99432
- const credentials = yield* CredentialService;
99433
- if (!credentials.load()) {
99434
- yield* log("Not logged in.");
99435
- return;
99436
- }
99437
- credentials.clear();
99438
- yield* log("\u2713 Logged out successfully.");
99439
- })
99440
- ).pipe(withDescription("Log out and clear stored credentials"));
99796
+ const logoutEffect = gen(function* () {
99797
+ const credentials = yield* CredentialService;
99798
+ const hadCredentials = yield* tryPromise({
99799
+ try: () => credentials.withLock(() => {
99800
+ const existed = credentials.load() !== null;
99801
+ credentials.clear();
99802
+ return existed;
99803
+ }),
99804
+ catch: (error) => error instanceof Error ? error : new Error("Could not clear saved credentials.")
99805
+ });
99806
+ if (!hadCredentials) {
99807
+ yield* log("Not logged in.");
99808
+ return;
99809
+ }
99810
+ yield* log("\u2713 Logged out successfully.");
99811
+ });
99812
+ const logoutCommand = make$4("logout", {}, () => logoutEffect).pipe(
99813
+ withDescription("Log out and clear stored credentials")
99814
+ );
99441
99815
 
99442
99816
  const refreshEffect = () => gen(function* () {
99443
99817
  const api = yield* ApiService;
@@ -99618,19 +99992,15 @@ const createCommand = make$4(
99618
99992
  const automationIdArg$6 = text$2({ name: "id" }).pipe(withDescription$5("Automation ID to delete"));
99619
99993
  const forceOption$3 = boolean("force").pipe(
99620
99994
  withAlias("f"),
99621
- withDescription$3("Skip confirmation prompt")
99995
+ withDescription$3("Confirm permanent deletion")
99622
99996
  );
99623
99997
  const deleteCommand$2 = make$4(
99624
99998
  "delete",
99625
99999
  { automationId: automationIdArg$6, org: orgOption, force: forceOption$3 },
99626
100000
  ({ automationId, org, force }) => gen(function* () {
100001
+ yield* requireConfirmation(force, "Automation deletion");
99627
100002
  const api = yield* ApiService;
99628
100003
  const orgSlug = yield* resolveOrgSlug(org, api);
99629
- if (!force) {
99630
- yield* log(`Warning: This will permanently delete automation "${automationId}".`);
99631
- yield* log("Use --force to skip this confirmation.");
99632
- return;
99633
- }
99634
100004
  yield* log(`Deleting automation ${automationId}...`);
99635
100005
  yield* api.deleteAutomation(orgSlug, automationId).pipe(
99636
100006
  catchTag(
@@ -100157,11 +100527,19 @@ const jsonOption$8 = boolean("json").pipe(
100157
100527
  );
100158
100528
  const listCommand$5 = make$4(
100159
100529
  "list",
100160
- { org: orgOption, json: jsonOption$8 },
100161
- ({ org, json }) => gen(function* () {
100530
+ {
100531
+ org: orgOption,
100532
+ json: jsonOption$8,
100533
+ cursor: cursorOption,
100534
+ jsonPage: jsonPageOption
100535
+ },
100536
+ ({ org, json, cursor, jsonPage }) => gen(function* () {
100537
+ yield* validatePageOptions(void 0, json, jsonPage);
100162
100538
  const api = yield* ApiService;
100163
100539
  const orgSlug = yield* resolveOrgSlug(org, api);
100164
- const result = yield* api.listConversations(orgSlug).pipe(
100540
+ const result = yield* api.listConversations(orgSlug, {
100541
+ cursor: cursor._tag === "Some" ? cursor.value : void 0
100542
+ }).pipe(
100165
100543
  catchTag(
100166
100544
  "NotLoggedInError",
100167
100545
  (e) => gen(function* () {
@@ -100177,10 +100555,16 @@ const listCommand$5 = make$4(
100177
100555
  })
100178
100556
  )
100179
100557
  );
100558
+ if (jsonPage) {
100559
+ yield* log(jsonFormat({ ...result, nextCursor: result.nextCursor ?? null }));
100560
+ return;
100561
+ }
100180
100562
  if (json) {
100181
100563
  yield* log(jsonFormat(result.conversations));
100564
+ yield* reportNextPage(result.nextCursor != null);
100182
100565
  return;
100183
100566
  }
100567
+ yield* reportNextPage(result.nextCursor != null);
100184
100568
  if (result.conversations.length === 0) {
100185
100569
  yield* log("No conversations found.");
100186
100570
  return;
@@ -100194,9 +100578,6 @@ const listCommand$5 = make$4(
100194
100578
  yield* log(` Updated: ${formatDate(conv.updatedAt)}`);
100195
100579
  yield* log("");
100196
100580
  }
100197
- if (result.nextCursor) {
100198
- yield* log("(more available)");
100199
- }
100200
100581
  })
100201
100582
  ).pipe(withDescription("List conversations"));
100202
100583
 
@@ -100427,20 +100808,24 @@ const appUrlOption = text("app-url").pipe(
100427
100808
  const setConfigEffect = (input) => gen(function* () {
100428
100809
  const configService = yield* ConfigService;
100429
100810
  const credentials = yield* CredentialService;
100430
- const currentConfig = yield* sync$1(() => configService.load());
100431
100811
  const appBaseUrl = yield* try_({
100432
100812
  try: () => normalizeAppUrl(input.appUrl),
100433
100813
  catch: (error) => error instanceof Error ? error : new Error(String(error))
100434
100814
  });
100435
- const shouldClearSession = currentConfig.appBaseUrl !== appBaseUrl;
100436
- const savedConfig = {
100437
- appBaseUrl,
100438
- currentOrganization: shouldClearSession ? void 0 : currentConfig.currentOrganization
100439
- };
100440
- yield* sync$1(() => configService.save(savedConfig, currentConfig));
100441
- if (shouldClearSession) {
100442
- yield* sync$1(() => credentials.clear());
100443
- }
100815
+ const { shouldClearSession, savedConfig } = yield* tryPromise({
100816
+ try: () => credentials.withLock(() => {
100817
+ const currentConfig = configService.load();
100818
+ const shouldClearSession2 = currentConfig.appBaseUrl !== appBaseUrl;
100819
+ const savedConfig2 = {
100820
+ appBaseUrl,
100821
+ currentOrganization: shouldClearSession2 ? void 0 : currentConfig.currentOrganization
100822
+ };
100823
+ if (shouldClearSession2) credentials.clear();
100824
+ configService.save(savedConfig2, currentConfig);
100825
+ return { shouldClearSession: shouldClearSession2, savedConfig: savedConfig2 };
100826
+ }),
100827
+ catch: (error) => error instanceof Error ? error : new Error("Could not save app configuration.")
100828
+ });
100444
100829
  yield* log("\n\u2713 Saved app configuration");
100445
100830
  yield* log(` App URL: ${appBaseUrl}`);
100446
100831
  if (shouldClearSession) {
@@ -100533,23 +100918,18 @@ const entryIdsArg = text$2({ name: "id" }).pipe(
100533
100918
  );
100534
100919
  const forceOption$2 = boolean("force").pipe(
100535
100920
  withAlias("f"),
100536
- withDescription$3("Skip confirmation prompt")
100921
+ withDescription$3("Confirm permanent deletion")
100537
100922
  );
100538
100923
  const deleteCommand$1 = make$4(
100539
100924
  "delete",
100540
100925
  { entryIds: entryIdsArg, org: orgOption, force: forceOption$2 },
100541
100926
  ({ entryIds, org, force }) => gen(function* () {
100927
+ yield* requireConfirmation(force, "Drive entry deletion");
100542
100928
  const api = yield* ApiService;
100543
100929
  const orgSlug = yield* resolveOrgSlug(org, api);
100544
100930
  const ids = Array.from(entryIds);
100545
100931
  if (ids.length === 0) {
100546
- yield* error$1("Please provide at least one entry ID to delete.");
100547
- return;
100548
- }
100549
- if (!force) {
100550
- yield* log(`Warning: This will permanently delete ${ids.length} drive entry/entries.`);
100551
- yield* log("Use --force to skip this confirmation.");
100552
- return;
100932
+ return yield* fail$3(new ApiError({ message: "Provide at least one entry ID to delete.", code: "CLI_INVALID_ARGUMENT", outcome: "not-dispatched" }));
100553
100933
  }
100554
100934
  yield* log(`Deleting ${ids.length} drive entry/entries...`);
100555
100935
  yield* api.deleteDriveEntries(orgSlug, ids).pipe(
@@ -100637,13 +101017,29 @@ File downloaded successfully!`);
100637
101017
  })
100638
101018
  ).pipe(withDescription("Download a file from My Drive"));
100639
101019
 
101020
+ const parentOption = text("parent-id").pipe(
101021
+ withDescription$3("List entries in this folder (default: root)"),
101022
+ optional
101023
+ );
100640
101024
  const listCommand$4 = make$4(
100641
101025
  "list",
100642
- { org: orgOption },
100643
- ({ org }) => gen(function* () {
101026
+ {
101027
+ org: orgOption,
101028
+ count: countOption,
101029
+ cursor: cursorOption,
101030
+ parentId: parentOption,
101031
+ jsonPage: jsonPageOption
101032
+ },
101033
+ ({ org, count, cursor, parentId, jsonPage }) => gen(function* () {
101034
+ const countValue = count._tag === "Some" ? count.value : void 0;
101035
+ yield* validatePageOptions(countValue, false, jsonPage);
100644
101036
  const api = yield* ApiService;
100645
101037
  const orgSlug = yield* resolveOrgSlug(org, api);
100646
- const result = yield* api.listDriveEntries(orgSlug).pipe(
101038
+ const result = yield* api.listDriveEntries(orgSlug, {
101039
+ count: countValue,
101040
+ cursor: cursor._tag === "Some" ? cursor.value : void 0,
101041
+ parentId: parentId._tag === "Some" ? parentId.value : void 0
101042
+ }).pipe(
100647
101043
  catchTag(
100648
101044
  "NotLoggedInError",
100649
101045
  (e) => gen(function* () {
@@ -100659,6 +101055,11 @@ const listCommand$4 = make$4(
100659
101055
  })
100660
101056
  )
100661
101057
  );
101058
+ if (jsonPage) {
101059
+ yield* log(jsonFormat({ ...result, nextCursor: result.nextCursor ?? null }));
101060
+ return;
101061
+ }
101062
+ yield* reportNextPage(result.hasNext);
100662
101063
  if (result.items.length === 0) {
100663
101064
  yield* log("No files found in drive.");
100664
101065
  return;
@@ -100672,9 +101073,6 @@ const listCommand$4 = make$4(
100672
101073
  yield* log(` Modified: ${formatDate(entry.modifiedAt)}`);
100673
101074
  yield* log("");
100674
101075
  }
100675
- if (result.hasNext) {
100676
- yield* log("More drive entries are available. Re-run with future pagination support to continue.");
100677
- }
100678
101076
  })
100679
101077
  ).pipe(withDescription("List files in My Drive"));
100680
101078
 
@@ -101412,12 +101810,9 @@ const exitCommand = make$4(
101412
101810
  "exit",
101413
101811
  { org: orgOption, force: forceOption$1 },
101414
101812
  ({ org, force }) => gen(function* () {
101813
+ yield* requireConfirmation(force, "Leaving the organization");
101415
101814
  const api = yield* ApiService;
101416
101815
  const orgSlug = yield* resolveOrgSlug(org, api);
101417
- if (!force) {
101418
- yield* log("Use --force (-f) to confirm leaving the organization.");
101419
- return;
101420
- }
101421
101816
  yield* api.exitOrg(orgSlug).pipe(
101422
101817
  catchTag(
101423
101818
  "NotLoggedInError",
@@ -101567,40 +101962,14 @@ const useCommand = make$4(
101567
101962
  "use",
101568
101963
  { slug: slugArg },
101569
101964
  ({ slug }) => gen(function* () {
101570
- const api = yield* ApiService;
101571
- const configService = yield* ConfigService;
101572
101965
  yield* log(`Selecting organization: ${slug}`);
101573
- const org = yield* api.getOrganization(slug).pipe(
101574
- catchTag(
101575
- "NotLoggedInError",
101576
- (e) => gen(function* () {
101577
- yield* error$1(e.message);
101578
- return yield* fail$3(e);
101579
- })
101580
- ),
101581
- catchTag(
101582
- "ApiError",
101583
- (e) => gen(function* () {
101584
- yield* error$1(`Failed to select organization: ${e.message}`);
101585
- return yield* fail$3(e);
101586
- })
101587
- )
101588
- );
101589
- if (org.ipBlocked) {
101590
- yield* error$1(`Organization "${slug}" is blocked due to IP restrictions.`);
101591
- return;
101592
- }
101593
- configService.save({
101594
- currentOrganization: {
101595
- id: org.id,
101596
- slug: org.slug,
101597
- name: org.name
101598
- }
101599
- });
101966
+ const org = yield* saveSelectedOrganization(slug);
101600
101967
  yield* log(`
101601
101968
  Switched to organization: ${org.name} (${org.slug})`);
101602
101969
  })
101603
- ).pipe(withDescription('Select an organization by slug for subsequent commands. Use "aipctl org select" if you do not know the slug.'));
101970
+ ).pipe(
101971
+ withDescription('Select an organization by slug for subsequent commands. Use "aipctl org select" if you do not know the slug.')
101972
+ );
101604
101973
 
101605
101974
  const orgCommandDescription = "Organization (list, current, use, select, members, capabilities, settings, exit)";
101606
101975
  const orgSubcommands = [
@@ -101671,19 +102040,15 @@ const cloneCommand = make$4(
101671
102040
  const skillIdArg$1 = text$2({ name: "id" }).pipe(withDescription$5("Skill ID to delete"));
101672
102041
  const forceOption = boolean("force").pipe(
101673
102042
  withAlias("f"),
101674
- withDescription$3("Skip confirmation prompt")
102043
+ withDescription$3("Confirm permanent deletion")
101675
102044
  );
101676
102045
  const deleteCommand = make$4(
101677
102046
  "delete",
101678
102047
  { skillId: skillIdArg$1, org: orgOption, force: forceOption },
101679
102048
  ({ skillId, org, force }) => gen(function* () {
102049
+ yield* requireConfirmation(force, "Skill deletion");
101680
102050
  const api = yield* ApiService;
101681
102051
  const orgSlug = yield* resolveOrgSlug(org, api);
101682
- if (!force) {
101683
- yield* log(`Warning: This will permanently delete skill "${skillId}".`);
101684
- yield* log("Use --force to skip this confirmation.");
101685
- return;
101686
- }
101687
102052
  yield* log(`Deleting skill ${skillId}...`);
101688
102053
  yield* api.deleteSkill(orgSlug, skillId).pipe(
101689
102054
  catchTag(
@@ -101792,12 +102157,18 @@ const searchOption$1 = text("search").pipe(
101792
102157
  );
101793
102158
  const libraryCommand = make$4(
101794
102159
  "library",
101795
- { org: orgOption, search: searchOption$1 },
101796
- ({ org, search }) => gen(function* () {
102160
+ { org: orgOption, search: searchOption$1, count: countOption, cursor: cursorOption, jsonPage: jsonPageOption },
102161
+ ({ org, search, count, cursor, jsonPage }) => gen(function* () {
102162
+ const countValue = count._tag === "Some" ? count.value : void 0;
102163
+ yield* validatePageOptions(countValue, false, jsonPage);
101797
102164
  const api = yield* ApiService;
101798
102165
  const orgSlug = yield* resolveOrgSlug(org, api);
101799
102166
  const searchValue = search._tag === "Some" ? search.value : void 0;
101800
- const result = yield* api.listLibrarySkills(orgSlug, { search: searchValue }).pipe(
102167
+ const result = yield* api.listLibrarySkills(orgSlug, {
102168
+ search: searchValue,
102169
+ count: countValue,
102170
+ cursor: cursor._tag === "Some" ? cursor.value : void 0
102171
+ }).pipe(
101801
102172
  catchTag(
101802
102173
  "NotLoggedInError",
101803
102174
  (e) => gen(function* () {
@@ -101813,6 +102184,11 @@ const libraryCommand = make$4(
101813
102184
  })
101814
102185
  )
101815
102186
  );
102187
+ if (jsonPage) {
102188
+ yield* log(jsonFormat({ ...result, nextCursor: result.nextCursor ?? null }));
102189
+ return;
102190
+ }
102191
+ yield* reportNextPage(result.hasNext);
101816
102192
  if (result.items.length === 0) {
101817
102193
  yield* log("No shared skills found.");
101818
102194
  return;
@@ -101830,9 +102206,6 @@ const libraryCommand = make$4(
101830
102206
  yield* log(` Uploaded: ${formatDate(skill.uploadedAt)}`);
101831
102207
  yield* log("");
101832
102208
  }
101833
- if (result.hasNext) {
101834
- yield* log("(More shared skills available.)");
101835
- }
101836
102209
  })
101837
102210
  ).pipe(withDescription("List shared skills from the library"));
101838
102211
 
@@ -101848,12 +102221,25 @@ const jsonOption = boolean("json").pipe(
101848
102221
  );
101849
102222
  const listCommand = make$4(
101850
102223
  "list",
101851
- { org: orgOption, search: searchOption, json: jsonOption },
101852
- ({ org, search, json }) => gen(function* () {
102224
+ {
102225
+ org: orgOption,
102226
+ search: searchOption,
102227
+ json: jsonOption,
102228
+ count: countOption,
102229
+ cursor: cursorOption,
102230
+ jsonPage: jsonPageOption
102231
+ },
102232
+ ({ org, search, json, count, cursor, jsonPage }) => gen(function* () {
102233
+ const countValue = count._tag === "Some" ? count.value : void 0;
102234
+ yield* validatePageOptions(countValue, json, jsonPage);
101853
102235
  const api = yield* ApiService;
101854
102236
  const orgSlug = yield* resolveOrgSlug(org, api);
101855
102237
  const searchValue = search._tag === "Some" ? search.value : void 0;
101856
- const result = yield* api.listSkills(orgSlug, { search: searchValue }).pipe(
102238
+ const result = yield* api.listSkills(orgSlug, {
102239
+ search: searchValue,
102240
+ count: countValue,
102241
+ cursor: cursor._tag === "Some" ? cursor.value : void 0
102242
+ }).pipe(
101857
102243
  catchTag(
101858
102244
  "NotLoggedInError",
101859
102245
  (e) => gen(function* () {
@@ -101869,10 +102255,16 @@ const listCommand = make$4(
101869
102255
  })
101870
102256
  )
101871
102257
  );
102258
+ if (jsonPage) {
102259
+ yield* log(jsonFormat({ ...result, nextCursor: result.nextCursor ?? null }));
102260
+ return;
102261
+ }
101872
102262
  if (json) {
101873
102263
  yield* log(jsonFormat(result.items));
102264
+ yield* reportNextPage(result.hasNext);
101874
102265
  return;
101875
102266
  }
102267
+ yield* reportNextPage(result.hasNext);
101876
102268
  if (result.items.length === 0) {
101877
102269
  yield* log("No skills found.");
101878
102270
  return;
@@ -101905,9 +102297,6 @@ const listCommand = make$4(
101905
102297
  }
101906
102298
  yield* log("");
101907
102299
  }
101908
- if (result.hasNext) {
101909
- yield* log("(More skills available. Use pagination to see all.)");
101910
- }
101911
102300
  })
101912
102301
  ).pipe(withDescription("List skills in the current (or specified) organization"));
101913
102302