@autohq/cli 0.1.104 → 0.1.106

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.
@@ -19873,7 +19873,14 @@ var ProviderGrantSchema = external_exports.object({
19873
19873
  providerResourceSelection: ProviderResourceSelectionSchema.default({
19874
19874
  kind: "all"
19875
19875
  }),
19876
- status: ProviderGrantStatusSchema
19876
+ status: ProviderGrantStatusSchema,
19877
+ /**
19878
+ * Scopes the server's current configuration requests that this grant has
19879
+ * not granted. Computed at read time against the deployed scope list, never
19880
+ * stored; present and non-empty only when a re-consent (`auto connect
19881
+ * <provider>`) is needed to pick up scopes added since the install.
19882
+ */
19883
+ missingScopes: external_exports.array(external_exports.string().trim().min(1)).optional()
19877
19884
  });
19878
19885
  var ProjectProviderAccessSchema = external_exports.object({
19879
19886
  providerGrantId: ProviderGrantIdSchema,
@@ -21630,7 +21637,14 @@ var SessionPresenceIdentitySchema = external_exports.object({
21630
21637
  // the presence icon endpoint.
21631
21638
  avatarSha256: external_exports.string().regex(SHA256_HEX_PATTERN).optional(),
21632
21639
  avatarUrl: external_exports.string().trim().min(1).optional(),
21633
- appliedIconSha256: external_exports.string().regex(SHA256_HEX_PATTERN).optional()
21640
+ appliedIconSha256: external_exports.string().regex(SHA256_HEX_PATTERN).optional(),
21641
+ /**
21642
+ * Scopes the current agent-app scope set requests that this realized
21643
+ * identity's install never granted. Computed at read time; non-empty means
21644
+ * the install predates a scope addition and `auto sessions connect
21645
+ * <session> --reconnect` refreshes it.
21646
+ */
21647
+ missingScopes: external_exports.array(external_exports.string().trim().min(1)).optional()
21634
21648
  });
21635
21649
  var SessionPresenceResponseSchema = external_exports.object({
21636
21650
  session: ResourceNameSchema,
@@ -21848,6 +21862,14 @@ var ProjectApplyAssetsSchema = external_exports.record(
21848
21862
  }),
21849
21863
  ProjectApplyAssetSchema
21850
21864
  );
21865
+ var AvatarAssetUploadRequestSchema = ProjectApplyAssetSchema.omit({
21866
+ sha256: true
21867
+ });
21868
+ var AvatarAssetUploadResponseSchema = external_exports.object({
21869
+ sha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
21870
+ contentType: external_exports.enum(AVATAR_ASSET_CONTENT_TYPES),
21871
+ sizeBytes: external_exports.number().int().positive()
21872
+ });
21851
21873
  var ProjectApplyRequestSchema = external_exports.object({
21852
21874
  delete: external_exports.array(ProjectDeleteResourceSchema).default([]),
21853
21875
  dryRun: external_exports.boolean().default(false),
@@ -26183,7 +26205,7 @@ Object.assign(lookup, {
26183
26205
  // package.json
26184
26206
  var package_default = {
26185
26207
  name: "@autohq/cli",
26186
- version: "0.1.104",
26208
+ version: "0.1.106",
26187
26209
  license: "SEE LICENSE IN README.md",
26188
26210
  publishConfig: {
26189
26211
  access: "public"
package/dist/index.js CHANGED
@@ -15410,7 +15410,14 @@ var init_provider_grants = __esm({
15410
15410
  providerResourceSelection: ProviderResourceSelectionSchema.default({
15411
15411
  kind: "all"
15412
15412
  }),
15413
- status: ProviderGrantStatusSchema
15413
+ status: ProviderGrantStatusSchema,
15414
+ /**
15415
+ * Scopes the server's current configuration requests that this grant has
15416
+ * not granted. Computed at read time against the deployed scope list, never
15417
+ * stored; present and non-empty only when a re-consent (`auto connect
15418
+ * <provider>`) is needed to pick up scopes added since the install.
15419
+ */
15420
+ missingScopes: external_exports.array(external_exports.string().trim().min(1)).optional()
15414
15421
  });
15415
15422
  ProjectProviderAccessSchema = external_exports.object({
15416
15423
  providerGrantId: ProviderGrantIdSchema,
@@ -17572,7 +17579,14 @@ var init_sessions = __esm({
17572
17579
  // the presence icon endpoint.
17573
17580
  avatarSha256: external_exports.string().regex(SHA256_HEX_PATTERN).optional(),
17574
17581
  avatarUrl: external_exports.string().trim().min(1).optional(),
17575
- appliedIconSha256: external_exports.string().regex(SHA256_HEX_PATTERN).optional()
17582
+ appliedIconSha256: external_exports.string().regex(SHA256_HEX_PATTERN).optional(),
17583
+ /**
17584
+ * Scopes the current agent-app scope set requests that this realized
17585
+ * identity's install never granted. Computed at read time; non-empty means
17586
+ * the install predates a scope addition and `auto sessions connect
17587
+ * <session> --reconnect` refreshes it.
17588
+ */
17589
+ missingScopes: external_exports.array(external_exports.string().trim().min(1)).optional()
17576
17590
  });
17577
17591
  SessionPresenceResponseSchema = external_exports.object({
17578
17592
  session: ResourceNameSchema,
@@ -17628,7 +17642,7 @@ var init_sessions = __esm({
17628
17642
  });
17629
17643
 
17630
17644
  // ../../packages/schemas/src/project-resources.ts
17631
- var EnvironmentApplyDocumentSchema, ProfileApplyDocumentSchema, ToolApplyDocumentSchema, SessionApplyDocumentSchema, ProjectApplyResourceSchema, PROJECT_RESOURCE_KINDS, ProjectDeleteResourceSchema, AVATAR_ASSET_CONTENT_TYPES, MAX_AVATAR_ASSET_BASE64_LENGTH, ProjectApplyAssetSchema, ProjectApplyAssetsSchema, ProjectApplyRequestSchema, ProjectApplySystemConfigSchema, ProjectAppliedResourceSchema, ProjectApplyDiagnosticSchema, ProjectApplyResponseSchema;
17645
+ var EnvironmentApplyDocumentSchema, ProfileApplyDocumentSchema, ToolApplyDocumentSchema, SessionApplyDocumentSchema, ProjectApplyResourceSchema, PROJECT_RESOURCE_KINDS, ProjectDeleteResourceSchema, AVATAR_ASSET_CONTENT_TYPES, MAX_AVATAR_ASSET_BASE64_LENGTH, ProjectApplyAssetSchema, ProjectApplyAssetsSchema, AvatarAssetUploadRequestSchema, AvatarAssetUploadResponseSchema, ProjectApplyRequestSchema, ProjectApplySystemConfigSchema, ProjectAppliedResourceSchema, ProjectApplyDiagnosticSchema, ProjectApplyResponseSchema;
17632
17646
  var init_project_resources = __esm({
17633
17647
  "../../packages/schemas/src/project-resources.ts"() {
17634
17648
  "use strict";
@@ -17678,6 +17692,14 @@ var init_project_resources = __esm({
17678
17692
  }),
17679
17693
  ProjectApplyAssetSchema
17680
17694
  );
17695
+ AvatarAssetUploadRequestSchema = ProjectApplyAssetSchema.omit({
17696
+ sha256: true
17697
+ });
17698
+ AvatarAssetUploadResponseSchema = external_exports.object({
17699
+ sha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
17700
+ contentType: external_exports.enum(AVATAR_ASSET_CONTENT_TYPES),
17701
+ sizeBytes: external_exports.number().int().positive()
17702
+ });
17681
17703
  ProjectApplyRequestSchema = external_exports.object({
17682
17704
  delete: external_exports.array(ProjectDeleteResourceSchema).default([]),
17683
17705
  dryRun: external_exports.boolean().default(false),
@@ -18489,7 +18511,7 @@ var init_package = __esm({
18489
18511
  "package.json"() {
18490
18512
  package_default = {
18491
18513
  name: "@autohq/cli",
18492
- version: "0.1.104",
18514
+ version: "0.1.106",
18493
18515
  license: "SEE LICENSE IN README.md",
18494
18516
  publishConfig: {
18495
18517
  access: "public"
@@ -18920,6 +18942,8 @@ function createResourceApi(context) {
18920
18942
  });
18921
18943
  return {
18922
18944
  applyEnvironmentResource: environments.apply,
18945
+ hasAvatarAsset: (request, options) => hasAvatarAsset(context, request, options ?? {}),
18946
+ uploadAvatarAsset: (request, options) => uploadAvatarAsset(context, request, options ?? {}),
18923
18947
  applyProjectResources: (request, options) => applyProjectResources(
18924
18948
  context,
18925
18949
  request,
@@ -18945,6 +18969,58 @@ function createResourceApi(context) {
18945
18969
  updateProjectServiceAccount: (request, options) => updateProjectServiceAccount(context, request, options ?? {})
18946
18970
  };
18947
18971
  }
18972
+ async function hasAvatarAsset(context, request, options) {
18973
+ const response = await context.authenticatedFetch(
18974
+ context.apiUrl(
18975
+ apiPath(`/avatars/${encodeURIComponent(request.sha256)}`),
18976
+ options.apiBaseUrl
18977
+ ),
18978
+ { method: "HEAD" },
18979
+ options.apiBaseUrl
18980
+ );
18981
+ if (response.ok) {
18982
+ return true;
18983
+ }
18984
+ if (response.status === 404) {
18985
+ return false;
18986
+ }
18987
+ throw new Error(await responseErrorMessage(response));
18988
+ }
18989
+ async function uploadAvatarAsset(context, request, options) {
18990
+ const path2 = await avatarAssetUploadPath(context, request.sha256);
18991
+ const response = await context.authenticatedFetch(
18992
+ context.apiUrl(path2, options.apiBaseUrl),
18993
+ {
18994
+ method: "PUT",
18995
+ headers: { "content-type": "application/json" },
18996
+ body: JSON.stringify({
18997
+ contentType: request.contentType,
18998
+ dataBase64: request.dataBase64
18999
+ })
19000
+ },
19001
+ options.apiBaseUrl
19002
+ );
19003
+ if (response.status === 404) {
19004
+ throw new AvatarAssetUploadUnsupportedError(
19005
+ await responseErrorMessage(response)
19006
+ );
19007
+ }
19008
+ if (!response.ok) {
19009
+ throw new Error(await responseErrorMessage(response));
19010
+ }
19011
+ return AvatarAssetUploadResponseSchema.parse(await response.json());
19012
+ }
19013
+ async function avatarAssetUploadPath(context, sha256) {
19014
+ const subpath = `/avatar-assets/${encodeURIComponent(sha256)}`;
19015
+ try {
19016
+ return projectApiPath(await context.activeProject(), subpath);
19017
+ } catch (error51) {
19018
+ if (context.shouldTryProjectInferredAuth()) {
19019
+ return apiPath(`/project${subpath}`);
19020
+ }
19021
+ throw error51;
19022
+ }
19023
+ }
18948
19024
  async function listProjectServiceAccounts(context, options) {
18949
19025
  const project = await context.activeProject();
18950
19026
  const response = await context.authenticatedFetch(
@@ -19249,13 +19325,15 @@ async function listProjectResources(context, endpoint, options) {
19249
19325
  }
19250
19326
  return await response.json();
19251
19327
  }
19252
- var SlackConfigTokenRequiredError;
19328
+ var AvatarAssetUploadUnsupportedError, SlackConfigTokenRequiredError;
19253
19329
  var init_resources2 = __esm({
19254
19330
  "src/lib/api/resources.ts"() {
19255
19331
  "use strict";
19256
19332
  init_src();
19257
19333
  init_errors3();
19258
19334
  init_paths();
19335
+ AvatarAssetUploadUnsupportedError = class extends Error {
19336
+ };
19259
19337
  SlackConfigTokenRequiredError = class extends Error {
19260
19338
  workspace;
19261
19339
  constructor(message, workspace) {
@@ -20935,6 +21013,98 @@ var init_connect = __esm({
20935
21013
  }
20936
21014
  });
20937
21015
 
21016
+ // src/commands/apply/assets.ts
21017
+ async function resolveApplyAssets(input) {
21018
+ const entries = Object.entries(input.request.assets);
21019
+ if (entries.length === 0) {
21020
+ return { resources: input.request.resources, assets: {} };
21021
+ }
21022
+ const probes = await Promise.all(
21023
+ entries.map(async ([path2, asset]) => ({
21024
+ path: path2,
21025
+ asset,
21026
+ stored: await input.client.hasAvatarAsset(
21027
+ { sha256: asset.sha256 },
21028
+ { apiBaseUrl: input.apiBaseUrl }
21029
+ )
21030
+ }))
21031
+ );
21032
+ const inline2 = {};
21033
+ for (const probe of probes) {
21034
+ if (probe.stored) {
21035
+ continue;
21036
+ }
21037
+ if (input.dryRun) {
21038
+ inline2[probe.path] = probe.asset;
21039
+ continue;
21040
+ }
21041
+ if (!await uploadAvatarAsset2(input.client, probe.asset, input.apiBaseUrl)) {
21042
+ inline2[probe.path] = probe.asset;
21043
+ }
21044
+ }
21045
+ const resources = input.request.resources.map(
21046
+ (resource) => stampAvatarSha256(resource, input.request.assets)
21047
+ );
21048
+ return { resources, assets: inline2 };
21049
+ }
21050
+ function assertApplyRequestBodySize(input) {
21051
+ const bytes = Buffer.byteLength(input.body, "utf8");
21052
+ if (bytes <= MAX_APPLY_REQUEST_BODY_BYTES) {
21053
+ return;
21054
+ }
21055
+ const paths = Object.keys(input.assets);
21056
+ const assetDetail = paths.length > 0 ? ` ${paths.length} avatar asset(s) are not yet stored on the server, so their bytes must ride this request: ${paths.join(", ")}. Apply the sessions referencing them in smaller batches (auto apply -f <file> --no-prune) or shrink the image files.` : "";
21057
+ throw new Error(
21058
+ `Apply request body is ${formatMegabytes(bytes)}MB, over the ~4.5MB server request limit.${assetDetail}`
21059
+ );
21060
+ }
21061
+ async function uploadAvatarAsset2(client, asset, apiBaseUrl) {
21062
+ try {
21063
+ await client.uploadAvatarAsset(asset, { apiBaseUrl });
21064
+ return true;
21065
+ } catch (error51) {
21066
+ if (error51 instanceof AvatarAssetUploadUnsupportedError) {
21067
+ return false;
21068
+ }
21069
+ throw error51;
21070
+ }
21071
+ }
21072
+ function stampAvatarSha256(resource, assets) {
21073
+ if (resource.kind !== "session") {
21074
+ return resource;
21075
+ }
21076
+ const identity2 = resource.spec.identity;
21077
+ const avatar = identity2?.avatar;
21078
+ if (!identity2 || !avatar) {
21079
+ return resource;
21080
+ }
21081
+ const asset = assets[avatar.asset];
21082
+ if (!asset) {
21083
+ return resource;
21084
+ }
21085
+ return {
21086
+ ...resource,
21087
+ spec: {
21088
+ ...resource.spec,
21089
+ identity: {
21090
+ ...identity2,
21091
+ avatar: { asset: avatar.asset, sha256: asset.sha256 }
21092
+ }
21093
+ }
21094
+ };
21095
+ }
21096
+ function formatMegabytes(bytes) {
21097
+ return (bytes / (1024 * 1024)).toFixed(1);
21098
+ }
21099
+ var MAX_APPLY_REQUEST_BODY_BYTES;
21100
+ var init_assets = __esm({
21101
+ "src/commands/apply/assets.ts"() {
21102
+ "use strict";
21103
+ init_resources2();
21104
+ MAX_APPLY_REQUEST_BODY_BYTES = 4 * 1024 * 1024;
21105
+ }
21106
+ });
21107
+
20938
21108
  // src/commands/apply/files.ts
20939
21109
  import { createHash as createHash2 } from "crypto";
20940
21110
  import {
@@ -21279,16 +21449,27 @@ async function applyResource(input) {
21279
21449
  }
21280
21450
  async function applyProjectInput(input) {
21281
21451
  const style = input.style ?? plainStyle;
21282
- const response = await input.client.applyProjectResources(
21283
- {
21284
- ...input.request.delete.length > 0 ? { delete: input.request.delete } : {},
21285
- dryRun: input.commandOptions.dryRun ?? input.request.dryRun,
21286
- prune: input.commandOptions.prune ?? input.request.prune,
21287
- resources: input.request.resources,
21288
- ...Object.keys(input.request.assets).length > 0 ? { assets: input.request.assets } : {}
21289
- },
21290
- { apiBaseUrl: input.commandOptions.apiBaseUrl }
21291
- );
21452
+ const dryRun = input.commandOptions.dryRun ?? input.request.dryRun;
21453
+ const resolved = await resolveApplyAssets({
21454
+ client: input.client,
21455
+ request: input.request,
21456
+ apiBaseUrl: input.commandOptions.apiBaseUrl,
21457
+ dryRun
21458
+ });
21459
+ const request = {
21460
+ ...input.request.delete.length > 0 ? { delete: input.request.delete } : {},
21461
+ dryRun,
21462
+ prune: input.commandOptions.prune ?? input.request.prune,
21463
+ resources: resolved.resources,
21464
+ ...Object.keys(resolved.assets).length > 0 ? { assets: resolved.assets } : {}
21465
+ };
21466
+ assertApplyRequestBodySize({
21467
+ body: JSON.stringify(request),
21468
+ assets: resolved.assets
21469
+ });
21470
+ const response = await input.client.applyProjectResources(request, {
21471
+ apiBaseUrl: input.commandOptions.apiBaseUrl
21472
+ });
21292
21473
  const resources = response.resources.map((resource, index) => ({
21293
21474
  kind: appliedResourceKind(input.request, response, index),
21294
21475
  resource
@@ -21391,6 +21572,7 @@ var init_actions = __esm({
21391
21572
  "use strict";
21392
21573
  init_style();
21393
21574
  init_connect();
21575
+ init_assets();
21394
21576
  init_files();
21395
21577
  }
21396
21578
  });
@@ -29263,7 +29445,7 @@ function connectionRows(connections, style) {
29263
29445
  provider: connection.provider,
29264
29446
  account: connection.externalAccount.loginOrName,
29265
29447
  grant: grant.name,
29266
- status: grant.status,
29448
+ status: stale(grant) ? "reconnect needed" : grant.status,
29267
29449
  projects: projectSummary({
29268
29450
  ...connection,
29269
29451
  projectAccess: connection.projectAccess.filter(
@@ -29305,7 +29487,23 @@ function connectionRows(connections, style) {
29305
29487
  grantStatusStyle(style, row.status)(row.status.padEnd(widths.status)),
29306
29488
  style.dim(row.projects)
29307
29489
  ].join(" ");
29308
- return [style.heading(headerLine), ...rows.map(format)];
29490
+ return [
29491
+ style.heading(headerLine),
29492
+ ...rows.map(format),
29493
+ ...staleGrantNotices(connections, style)
29494
+ ];
29495
+ }
29496
+ function staleGrantNotices(connections, style) {
29497
+ return connections.flatMap(
29498
+ (connection) => connection.grants.filter(stale).map(
29499
+ (grant) => style.warn(
29500
+ `grant ${grant.name}: installed before scopes ${(grant.missingScopes ?? []).join(", ")} were added \u2014 re-run \`auto connect ${connection.provider}\` to re-consent`
29501
+ )
29502
+ )
29503
+ );
29504
+ }
29505
+ function stale(grant) {
29506
+ return grant.status === "active" && (grant.missingScopes?.length ?? 0) > 0;
29309
29507
  }
29310
29508
  function providerRows(providers, style) {
29311
29509
  if (providers.length === 0) {
@@ -29346,6 +29544,8 @@ function grantStatusStyle(style, status) {
29346
29544
  switch (status) {
29347
29545
  case "active":
29348
29546
  return style.success;
29547
+ case "reconnect needed":
29548
+ return style.warn;
29349
29549
  case "error":
29350
29550
  return style.error;
29351
29551
  case "revoked":
@@ -32016,6 +32216,10 @@ async function connectSessionPresence2(input) {
32016
32216
  input.writeOutput(result.message);
32017
32217
  for (const identity2 of result.realized) {
32018
32218
  input.writeOutput(realizedIdentityLine(input.session, identity2));
32219
+ const staleNotice = staleScopesLine(input.session, identity2);
32220
+ if (staleNotice) {
32221
+ input.writeOutput(staleNotice);
32222
+ }
32019
32223
  }
32020
32224
  if (result.pending.length === 0) {
32021
32225
  await promptForIconUploads(input, options);
@@ -32094,6 +32298,12 @@ function realizedIdentityLine(session, identity2) {
32094
32298
  const handle = identity2.botUsername ? `persona/@${identity2.botUsername}` : `bot/${identity2.botUserId ?? ""}`;
32095
32299
  return `connected session/${session} workspace/${identity2.workspace} ${handle}`;
32096
32300
  }
32301
+ function staleScopesLine(session, identity2) {
32302
+ if (!identity2.missingScopes?.length) {
32303
+ return void 0;
32304
+ }
32305
+ return ` workspace "${identity2.workspace}" was installed before scopes ${identity2.missingScopes.join(", ")} were added; refresh with \`auto sessions connect ${session} --reconnect\``;
32306
+ }
32097
32307
  function manualGuidance(input) {
32098
32308
  if (input.allTelegram) {
32099
32309
  return `Open each creation link and confirm the new bot in Telegram; Auto provisions it automatically. Re-run \`auto sessions connect ${input.session}\` (or check presence) to see it realize.`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autohq/cli",
3
- "version": "0.1.104",
3
+ "version": "0.1.106",
4
4
  "license": "SEE LICENSE IN README.md",
5
5
  "publishConfig": {
6
6
  "access": "public"