@autohq/cli 0.1.143 → 0.1.144

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.
@@ -20782,6 +20782,44 @@ var ConnectionRemoveResponseSchema = external_exports.object({
20782
20782
  message: external_exports.string().trim().min(1)
20783
20783
  });
20784
20784
 
20785
+ // ../../packages/schemas/src/github-sync.ts
20786
+ var GITHUB_SYNC_AUTO_PATH = ".auto";
20787
+ var GithubSyncRepositoryFullNameSchema = external_exports.string().trim().min(1).regex(/^[^/\s]+\/[^/\s]+$/, {
20788
+ message: "repository must be in owner/name form"
20789
+ });
20790
+ var GithubSyncProductionBranchSchema = external_exports.string().trim().min(1).max(255).refine((branch) => !branch.startsWith("/") && !branch.endsWith("/"), {
20791
+ message: "branch must not start or end with /"
20792
+ });
20793
+ var GithubSyncBindingSchema = external_exports.object({
20794
+ id: external_exports.string().trim().min(1),
20795
+ organizationId: OrganizationIdSchema,
20796
+ projectId: ProjectIdSchema,
20797
+ providerGrantId: ProviderGrantIdSchema,
20798
+ installationId: external_exports.string().trim().min(1),
20799
+ repoFullName: GithubSyncRepositoryFullNameSchema,
20800
+ repoId: external_exports.string().trim().min(1).nullable(),
20801
+ productionBranch: GithubSyncProductionBranchSchema,
20802
+ autoPath: external_exports.literal(GITHUB_SYNC_AUTO_PATH),
20803
+ enabled: external_exports.boolean(),
20804
+ lastPlannedSha: external_exports.string().trim().min(1).nullable(),
20805
+ lastAppliedSha: external_exports.string().trim().min(1).nullable(),
20806
+ createdAt: external_exports.string().datetime(),
20807
+ updatedAt: external_exports.string().datetime()
20808
+ });
20809
+ var GithubSyncBindingCreateRequestSchema = external_exports.object({
20810
+ connection: external_exports.string().trim().min(1).optional(),
20811
+ repo: GithubSyncRepositoryFullNameSchema,
20812
+ repoId: external_exports.string().trim().min(1).optional(),
20813
+ branch: GithubSyncProductionBranchSchema.optional()
20814
+ });
20815
+ var GithubSyncBindingCreateResponseSchema = external_exports.object({
20816
+ binding: GithubSyncBindingSchema,
20817
+ created: external_exports.boolean()
20818
+ });
20819
+ var GithubSyncBindingListResponseSchema = external_exports.object({
20820
+ bindings: external_exports.array(GithubSyncBindingSchema)
20821
+ });
20822
+
20785
20823
  // ../../packages/schemas/src/github-credentials.ts
20786
20824
  var CreateRunGithubCredentialRequestSchema = external_exports.object({
20787
20825
  host: external_exports.literal("github.com"),
@@ -26236,7 +26274,7 @@ Object.assign(lookup, {
26236
26274
  // package.json
26237
26275
  var package_default = {
26238
26276
  name: "@autohq/cli",
26239
- version: "0.1.143",
26277
+ version: "0.1.144",
26240
26278
  license: "SEE LICENSE IN README.md",
26241
26279
  publishConfig: {
26242
26280
  access: "public"
package/dist/index.js CHANGED
@@ -17328,6 +17328,52 @@ var init_connections = __esm({
17328
17328
  }
17329
17329
  });
17330
17330
 
17331
+ // ../../packages/schemas/src/github-sync.ts
17332
+ var GITHUB_SYNC_AUTO_PATH, GithubSyncRepositoryFullNameSchema, GithubSyncProductionBranchSchema, GithubSyncBindingSchema, GithubSyncBindingCreateRequestSchema, GithubSyncBindingCreateResponseSchema, GithubSyncBindingListResponseSchema;
17333
+ var init_github_sync = __esm({
17334
+ "../../packages/schemas/src/github-sync.ts"() {
17335
+ "use strict";
17336
+ init_zod();
17337
+ init_ids();
17338
+ GITHUB_SYNC_AUTO_PATH = ".auto";
17339
+ GithubSyncRepositoryFullNameSchema = external_exports.string().trim().min(1).regex(/^[^/\s]+\/[^/\s]+$/, {
17340
+ message: "repository must be in owner/name form"
17341
+ });
17342
+ GithubSyncProductionBranchSchema = external_exports.string().trim().min(1).max(255).refine((branch) => !branch.startsWith("/") && !branch.endsWith("/"), {
17343
+ message: "branch must not start or end with /"
17344
+ });
17345
+ GithubSyncBindingSchema = external_exports.object({
17346
+ id: external_exports.string().trim().min(1),
17347
+ organizationId: OrganizationIdSchema,
17348
+ projectId: ProjectIdSchema,
17349
+ providerGrantId: ProviderGrantIdSchema,
17350
+ installationId: external_exports.string().trim().min(1),
17351
+ repoFullName: GithubSyncRepositoryFullNameSchema,
17352
+ repoId: external_exports.string().trim().min(1).nullable(),
17353
+ productionBranch: GithubSyncProductionBranchSchema,
17354
+ autoPath: external_exports.literal(GITHUB_SYNC_AUTO_PATH),
17355
+ enabled: external_exports.boolean(),
17356
+ lastPlannedSha: external_exports.string().trim().min(1).nullable(),
17357
+ lastAppliedSha: external_exports.string().trim().min(1).nullable(),
17358
+ createdAt: external_exports.string().datetime(),
17359
+ updatedAt: external_exports.string().datetime()
17360
+ });
17361
+ GithubSyncBindingCreateRequestSchema = external_exports.object({
17362
+ connection: external_exports.string().trim().min(1).optional(),
17363
+ repo: GithubSyncRepositoryFullNameSchema,
17364
+ repoId: external_exports.string().trim().min(1).optional(),
17365
+ branch: GithubSyncProductionBranchSchema.optional()
17366
+ });
17367
+ GithubSyncBindingCreateResponseSchema = external_exports.object({
17368
+ binding: GithubSyncBindingSchema,
17369
+ created: external_exports.boolean()
17370
+ });
17371
+ GithubSyncBindingListResponseSchema = external_exports.object({
17372
+ bindings: external_exports.array(GithubSyncBindingSchema)
17373
+ });
17374
+ }
17375
+ });
17376
+
17331
17377
  // ../../packages/schemas/src/github-credentials.ts
17332
17378
  var CreateRunGithubCredentialRequestSchema, RunGithubCredentialResponseSchema;
17333
17379
  var init_github_credentials = __esm({
@@ -19393,6 +19439,7 @@ var init_src = __esm({
19393
19439
  init_conversation();
19394
19440
  init_conversation_reducer();
19395
19441
  init_connections();
19442
+ init_github_sync();
19396
19443
  init_github_credentials();
19397
19444
  init_github_mcp_catalog();
19398
19445
  init_identities();
@@ -20646,6 +20693,42 @@ function createApiClient(input) {
20646
20693
  }
20647
20694
  return await response.json();
20648
20695
  },
20696
+ async createGithubSyncBinding(request, options = {}) {
20697
+ const project = await activeProject();
20698
+ const response = await authenticatedFetch(
20699
+ apiUrl(
20700
+ projectApiPath(project, "/github/sync/bindings"),
20701
+ options.apiBaseUrl
20702
+ ),
20703
+ {
20704
+ method: "POST",
20705
+ headers: {
20706
+ "content-type": "application/json"
20707
+ },
20708
+ body: JSON.stringify(request)
20709
+ },
20710
+ options.apiBaseUrl
20711
+ );
20712
+ if (!response.ok) {
20713
+ throw new Error(await responseErrorMessage(response));
20714
+ }
20715
+ return GithubSyncBindingCreateResponseSchema.parse(await response.json());
20716
+ },
20717
+ async listGithubSyncBindings(options = {}) {
20718
+ const project = await activeProject();
20719
+ const response = await authenticatedFetch(
20720
+ apiUrl(
20721
+ projectApiPath(project, "/github/sync/bindings"),
20722
+ options.apiBaseUrl
20723
+ ),
20724
+ {},
20725
+ options.apiBaseUrl
20726
+ );
20727
+ if (!response.ok) {
20728
+ throw new Error(await responseErrorMessage(response));
20729
+ }
20730
+ return GithubSyncBindingListResponseSchema.parse(await response.json());
20731
+ },
20649
20732
  ...createResourceApi({
20650
20733
  activeProject,
20651
20734
  apiUrl,
@@ -21169,7 +21252,7 @@ var init_package = __esm({
21169
21252
  "package.json"() {
21170
21253
  package_default = {
21171
21254
  name: "@autohq/cli",
21172
- version: "0.1.143",
21255
+ version: "0.1.144",
21173
21256
  license: "SEE LICENSE IN README.md",
21174
21257
  publishConfig: {
21175
21258
  access: "public"
@@ -25113,11 +25196,12 @@ import { Box as Box8, Text as Text8 } from "ink";
25113
25196
  import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
25114
25197
  function ConnectionsView({
25115
25198
  connections,
25199
+ syncBindings = [],
25116
25200
  selectedIndex,
25117
25201
  activeProjectId,
25118
25202
  error: error51
25119
25203
  }) {
25120
- const rows = connectionRows2(connections, activeProjectId);
25204
+ const rows = connectionRows2(connections, activeProjectId, syncBindings);
25121
25205
  if (error51) {
25122
25206
  return /* @__PURE__ */ jsx8(Box8, { paddingX: 2, paddingTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: "red", children: error51 }) });
25123
25207
  }
@@ -25128,7 +25212,8 @@ function ConnectionsView({
25128
25212
  /* @__PURE__ */ jsx8(HeaderCell, { width: COL_GRANT, children: "CONNECTION" }),
25129
25213
  /* @__PURE__ */ jsx8(HeaderCell, { width: COL_STATUS, children: "STATUS" }),
25130
25214
  /* @__PURE__ */ jsx8(HeaderCell, { width: COL_ACTIVE, children: "PROJECT" }),
25131
- /* @__PURE__ */ jsx8(HeaderCell, { width: COL_PROJECTS, children: "ALLOWED PROJECTS" })
25215
+ /* @__PURE__ */ jsx8(HeaderCell, { width: COL_PROJECTS, children: "ALLOWED PROJECTS" }),
25216
+ /* @__PURE__ */ jsx8(HeaderCell, { width: COL_SYNC, children: "SYNC" })
25132
25217
  ] }),
25133
25218
  rows.length === 0 ? /* @__PURE__ */ jsx8(Box8, { paddingX: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "no connections found" }) }) : rows.map((row, i) => {
25134
25219
  const isSelected = i === selectedIndex;
@@ -25157,12 +25242,13 @@ function ConnectionsView({
25157
25242
  children: activeProjectId ? row.activeProjectAllowed ? "allowed" : "-" : "-"
25158
25243
  }
25159
25244
  ) }),
25160
- /* @__PURE__ */ jsx8(TableCell, { width: COL_PROJECTS, selected: isSelected, dim, children: row.projects })
25245
+ /* @__PURE__ */ jsx8(TableCell, { width: COL_PROJECTS, selected: isSelected, dim, children: row.projects }),
25246
+ /* @__PURE__ */ jsx8(TableCell, { width: COL_SYNC, selected: isSelected, dim, children: row.sync })
25161
25247
  ] }, row.key);
25162
25248
  })
25163
25249
  ] });
25164
25250
  }
25165
- function connectionRows2(connections, activeProjectId) {
25251
+ function connectionRows2(connections, activeProjectId, syncBindings = []) {
25166
25252
  return connections.flatMap(
25167
25253
  (connection) => connection.grants.map((grant) => {
25168
25254
  const grantAccess = connection.projectAccess.filter(
@@ -25171,6 +25257,7 @@ function connectionRows2(connections, activeProjectId) {
25171
25257
  const projects = [
25172
25258
  ...new Set(grantAccess.map((access) => access.projectId))
25173
25259
  ].sort();
25260
+ const sync = syncBindings.filter((binding) => binding.providerGrantId === grant.id).map((binding) => `${binding.repoFullName}@${binding.productionBranch}`).sort();
25174
25261
  return {
25175
25262
  key: `${connection.provider}-${connection.externalAccount.id}-${grant.id}`,
25176
25263
  provider: connection.provider,
@@ -25180,24 +25267,26 @@ function connectionRows2(connections, activeProjectId) {
25180
25267
  projects: projects.length > 0 ? projects.join(",") : "-",
25181
25268
  activeProjectAllowed: grantAccess.some(
25182
25269
  (access) => access.projectId === activeProjectId
25183
- )
25270
+ ),
25271
+ sync: sync.length > 0 ? sync.join(",") : "-"
25184
25272
  };
25185
25273
  })
25186
25274
  );
25187
25275
  }
25188
- function connectionListFingerprint(connections, activeProjectId) {
25189
- return connectionRows2(connections, activeProjectId).map(
25276
+ function connectionListFingerprint(connections, activeProjectId, syncBindings = []) {
25277
+ return connectionRows2(connections, activeProjectId, syncBindings).map(
25190
25278
  (row) => [
25191
25279
  row.provider,
25192
25280
  row.account,
25193
25281
  row.grant,
25194
25282
  row.status,
25195
25283
  row.projects,
25196
- row.activeProjectAllowed ? "1" : "0"
25284
+ row.activeProjectAllowed ? "1" : "0",
25285
+ row.sync
25197
25286
  ].join("")
25198
25287
  ).join("");
25199
25288
  }
25200
- var COL_PROVIDER2, COL_ACCOUNT, COL_GRANT, COL_STATUS, COL_ACTIVE, COL_PROJECTS;
25289
+ var COL_PROVIDER2, COL_ACCOUNT, COL_GRANT, COL_STATUS, COL_ACTIVE, COL_PROJECTS, COL_SYNC;
25201
25290
  var init_ConnectionsView = __esm({
25202
25291
  "src/tui/ConnectionsView.tsx"() {
25203
25292
  "use strict";
@@ -25207,7 +25296,8 @@ var init_ConnectionsView = __esm({
25207
25296
  COL_GRANT = 22;
25208
25297
  COL_STATUS = 8;
25209
25298
  COL_ACTIVE = 8;
25210
- COL_PROJECTS = 28;
25299
+ COL_PROJECTS = 22;
25300
+ COL_SYNC = 30;
25211
25301
  }
25212
25302
  });
25213
25303
 
@@ -25546,6 +25636,10 @@ function useConnectionProviders(apiUrl, enabled = true) {
25546
25636
  const client = useApiClient();
25547
25637
  return useQuery(connectionProvidersQueryOptions(client, apiUrl, enabled));
25548
25638
  }
25639
+ function useGithubSyncBindings(apiUrl, enabled = true) {
25640
+ const client = useApiClient();
25641
+ return useQuery(githubSyncBindingsQueryOptions(client, apiUrl, enabled));
25642
+ }
25549
25643
  function useCreateServiceAccount(apiUrl) {
25550
25644
  const client = useApiClient();
25551
25645
  const queryClient = useQueryClient();
@@ -25593,6 +25687,7 @@ function useSwitchProject() {
25593
25687
  ["environments"],
25594
25688
  ["service-accounts"],
25595
25689
  ["connections"],
25690
+ ["github-sync-bindings"],
25596
25691
  ["connection-providers"]
25597
25692
  ]) {
25598
25693
  void queryClient.invalidateQueries({ queryKey });
@@ -25664,6 +25759,32 @@ function useAllowConnection(apiUrl) {
25664
25759
  })
25665
25760
  });
25666
25761
  }
25762
+ function useCreateGithubSyncBinding(apiUrl) {
25763
+ const client = useApiClient();
25764
+ const queryClient = useQueryClient();
25765
+ return useMutation({
25766
+ mutationFn: ({
25767
+ connection,
25768
+ repo,
25769
+ branch
25770
+ }) => client.createGithubSyncBinding(
25771
+ {
25772
+ connection,
25773
+ repo,
25774
+ ...branch ? { branch } : {}
25775
+ },
25776
+ { apiBaseUrl: apiUrl }
25777
+ ),
25778
+ onSuccess: () => {
25779
+ void queryClient.invalidateQueries({
25780
+ queryKey: queryKeys.githubSyncBindings(apiUrl)
25781
+ });
25782
+ void queryClient.invalidateQueries({
25783
+ queryKey: queryKeys.connections(apiUrl)
25784
+ });
25785
+ }
25786
+ });
25787
+ }
25667
25788
  function useRemoveConnection(apiUrl) {
25668
25789
  const client = useApiClient();
25669
25790
  const queryClient = useQueryClient();
@@ -25854,6 +25975,17 @@ function connectionProvidersQueryOptions(client, apiUrl, enabled = true) {
25854
25975
  staleTime: Number.POSITIVE_INFINITY
25855
25976
  });
25856
25977
  }
25978
+ function githubSyncBindingsQueryOptions(client, apiUrl, enabled = true) {
25979
+ return queryOptions({
25980
+ queryKey: queryKeys.githubSyncBindings(apiUrl),
25981
+ queryFn: async () => {
25982
+ const res = await client.listGithubSyncBindings({ apiBaseUrl: apiUrl });
25983
+ return res.bindings;
25984
+ },
25985
+ enabled,
25986
+ staleTime: Number.POSITIVE_INFINITY
25987
+ });
25988
+ }
25857
25989
  function sortAgents(list) {
25858
25990
  return list.slice().sort(
25859
25991
  (a, b) => (a.spec.environment ?? "").localeCompare(b.spec.environment ?? "") || a.metadata.name.localeCompare(b.metadata.name)
@@ -25890,6 +26022,7 @@ var init_queries = __esm({
25890
26022
  projects: (apiUrl) => ["projects", apiUrl],
25891
26023
  serviceAccounts: (apiUrl) => ["service-accounts", apiUrl],
25892
26024
  connections: (apiUrl) => ["connections", apiUrl],
26025
+ githubSyncBindings: (apiUrl) => ["github-sync-bindings", apiUrl],
25893
26026
  connectionProviders: (apiUrl) => ["connection-providers", apiUrl],
25894
26027
  runs: (apiUrl, includeArchived = false) => ["runs", apiUrl, includeArchived],
25895
26028
  agentRuns: (agentName, apiUrl, includeArchived = false) => ["agent-runs", agentName, apiUrl, includeArchived]
@@ -26329,6 +26462,9 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26329
26462
  const [connectionMode, setConnectionMode] = useState3("connections");
26330
26463
  const [serviceAccountInputMode, setServiceAccountInputMode] = useState3(null);
26331
26464
  const [serviceAccountNameInput, setServiceAccountNameInput] = useState3("");
26465
+ const [githubSyncInputMode, setGithubSyncInputMode] = useState3(null);
26466
+ const [githubSyncRepoInput, setGithubSyncRepoInput] = useState3("");
26467
+ const [githubSyncBranchInput, setGithubSyncBranchInput] = useState3("");
26332
26468
  const [inspectedResource, setInspectedResource] = useState3(null);
26333
26469
  const [inspectScrollOffset, setInspectScrollOffset] = useState3(0);
26334
26470
  const localAgentAuthoringStatuses = useMemo3(
@@ -26382,10 +26518,15 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26382
26518
  apiUrl,
26383
26519
  Boolean(activeSelectionIds.organizationId)
26384
26520
  );
26521
+ const { data: githubSyncBindings = [] } = useGithubSyncBindings(
26522
+ apiUrl,
26523
+ Boolean(activeSelectionIds.projectId)
26524
+ );
26385
26525
  const switchSelection = useSwitchProject();
26386
26526
  const startConnection = useStartConnection(apiUrl);
26387
26527
  const allowConnection = useAllowConnection(apiUrl);
26388
26528
  const removeConnection = useRemoveConnection(apiUrl);
26529
+ const createGithubSyncBinding = useCreateGithubSyncBinding(apiUrl);
26389
26530
  const createServiceAccount2 = useCreateServiceAccount(apiUrl);
26390
26531
  const rotateServiceAccount2 = useRotateServiceAccount(apiUrl);
26391
26532
  const removeServiceAccount2 = useRemoveServiceAccount(apiUrl);
@@ -26430,11 +26571,13 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26430
26571
  );
26431
26572
  const selectedConnection = connectionRows2(
26432
26573
  connections,
26433
- activeSelectionIds.projectId
26574
+ activeSelectionIds.projectId,
26575
+ githubSyncBindings
26434
26576
  )[connectionIndex];
26435
26577
  const connectionFingerprint = connectionListFingerprint(
26436
26578
  connections,
26437
- activeSelectionIds.projectId
26579
+ activeSelectionIds.projectId,
26580
+ githubSyncBindings
26438
26581
  );
26439
26582
  const connectionProviderRowCount = connectionProviders.length;
26440
26583
  const selectedConnectionProvider = providerRows2(connectionProviders)[connectionProviderIndex];
@@ -26526,7 +26669,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26526
26669
  );
26527
26670
  if (index >= 0) setEnvironmentIndex(index);
26528
26671
  }, [environments, selectedAgent]);
26529
- const connectionStatus = startConnection.error instanceof Error ? startConnection.error.message : allowConnection.error instanceof Error ? allowConnection.error.message : removeConnection.error instanceof Error ? removeConnection.error.message : connectionNotice;
26672
+ const connectionStatus = startConnection.error instanceof Error ? startConnection.error.message : allowConnection.error instanceof Error ? allowConnection.error.message : removeConnection.error instanceof Error ? removeConnection.error.message : createGithubSyncBinding.error instanceof Error ? createGithubSyncBinding.error.message : connectionNotice;
26530
26673
  const startRun = useCallback2(() => {
26531
26674
  if (!scopedAgent || triggerRun.isPending) return;
26532
26675
  triggerRun.reset();
@@ -26736,7 +26879,8 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26736
26879
  );
26737
26880
  const nextFingerprint = connectionListFingerprint(
26738
26881
  response.connections,
26739
- activeSelectionIds.projectId
26882
+ activeSelectionIds.projectId,
26883
+ githubSyncBindings
26740
26884
  );
26741
26885
  if (nextFingerprint !== input.beforeFingerprint) {
26742
26886
  setConnectionNotice(`${input.provider} connection status updated.`);
@@ -26767,14 +26911,24 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26767
26911
  void refresh();
26768
26912
  }, CONNECTION_REFRESH_INTERVAL_MS);
26769
26913
  },
26770
- [activeSelectionIds.projectId, apiUrl, client, queryClient]
26914
+ [
26915
+ activeSelectionIds.projectId,
26916
+ apiUrl,
26917
+ client,
26918
+ githubSyncBindings,
26919
+ queryClient
26920
+ ]
26771
26921
  );
26772
26922
  const beginConnection = useCallback2(
26773
26923
  (provider) => {
26774
26924
  startConnection.reset();
26775
26925
  allowConnection.reset();
26776
26926
  removeConnection.reset();
26927
+ createGithubSyncBinding.reset();
26777
26928
  setPendingRemoveConnectionKey(null);
26929
+ setGithubSyncInputMode(null);
26930
+ setGithubSyncRepoInput("");
26931
+ setGithubSyncBranchInput("");
26778
26932
  setConnectionNotice(null);
26779
26933
  startConnection.mutate(
26780
26934
  {
@@ -26806,6 +26960,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26806
26960
  allowConnection,
26807
26961
  connectionFingerprint,
26808
26962
  removeConnection,
26963
+ createGithubSyncBinding,
26809
26964
  refreshConnectionsUntilChanged,
26810
26965
  startConnection
26811
26966
  ]
@@ -26843,6 +26998,88 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26843
26998
  serviceAccountNameInput,
26844
26999
  serviceAccounts
26845
27000
  ]);
27001
+ const beginGithubSyncBinding = useCallback2(() => {
27002
+ startConnection.reset();
27003
+ allowConnection.reset();
27004
+ removeConnection.reset();
27005
+ createGithubSyncBinding.reset();
27006
+ setPendingRemoveConnectionKey(null);
27007
+ if (!selectedConnection) {
27008
+ setConnectionNotice("Select a GitHub connection before enabling Sync.");
27009
+ return;
27010
+ }
27011
+ if (selectedConnection.provider !== "github") {
27012
+ setConnectionNotice("Sync v1 supports GitHub connections only.");
27013
+ return;
27014
+ }
27015
+ if (!activeSelectionIds.projectId) {
27016
+ setConnectionNotice("Select an active project before enabling Sync.");
27017
+ return;
27018
+ }
27019
+ if (!selectedConnection.activeProjectAllowed) {
27020
+ setConnectionNotice(
27021
+ "Allow this GitHub connection for the active project first."
27022
+ );
27023
+ return;
27024
+ }
27025
+ setConnectionNotice(null);
27026
+ setGithubSyncRepoInput("");
27027
+ setGithubSyncBranchInput("");
27028
+ setGithubSyncInputMode("repo");
27029
+ }, [
27030
+ activeSelectionIds.projectId,
27031
+ allowConnection,
27032
+ createGithubSyncBinding,
27033
+ removeConnection,
27034
+ selectedConnection,
27035
+ startConnection
27036
+ ]);
27037
+ const submitGithubSyncRepo = useCallback2(() => {
27038
+ const repo = githubSyncRepoInput.trim();
27039
+ if (!repo) {
27040
+ setConnectionNotice("Enter a GitHub repository in owner/name form.");
27041
+ return;
27042
+ }
27043
+ setConnectionNotice(null);
27044
+ setGithubSyncInputMode("branch");
27045
+ }, [githubSyncRepoInput]);
27046
+ const submitGithubSyncBranch = useCallback2(() => {
27047
+ if (!selectedConnection) {
27048
+ setGithubSyncInputMode(null);
27049
+ setConnectionNotice("Select a GitHub connection before enabling Sync.");
27050
+ return;
27051
+ }
27052
+ const repo = githubSyncRepoInput.trim();
27053
+ const branch = githubSyncBranchInput.trim();
27054
+ if (!repo) {
27055
+ setGithubSyncInputMode("repo");
27056
+ setConnectionNotice("Enter a GitHub repository in owner/name form.");
27057
+ return;
27058
+ }
27059
+ setConnectionNotice(null);
27060
+ createGithubSyncBinding.mutate(
27061
+ {
27062
+ connection: selectedConnection.grant,
27063
+ repo,
27064
+ ...branch ? { branch } : {}
27065
+ },
27066
+ {
27067
+ onSuccess: (result) => {
27068
+ setGithubSyncInputMode(null);
27069
+ setGithubSyncRepoInput("");
27070
+ setGithubSyncBranchInput("");
27071
+ setConnectionNotice(
27072
+ `${result.created ? "Enabled" : "Updated"} Sync for ${result.binding.repoFullName} on ${result.binding.productionBranch}.`
27073
+ );
27074
+ }
27075
+ }
27076
+ );
27077
+ }, [
27078
+ createGithubSyncBinding,
27079
+ githubSyncBranchInput,
27080
+ githubSyncRepoInput,
27081
+ selectedConnection
27082
+ ]);
26846
27083
  const selectTopLevelSection = useCallback2(
26847
27084
  (section) => {
26848
27085
  if (section === "agents") {
@@ -26881,6 +27118,11 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26881
27118
  setPendingRemoveServiceAccountName(null);
26882
27119
  setPendingRotateServiceAccountName(null);
26883
27120
  }
27121
+ if (activeSection !== "connections") {
27122
+ setGithubSyncInputMode(null);
27123
+ setGithubSyncRepoInput("");
27124
+ setGithubSyncBranchInput("");
27125
+ }
26884
27126
  }, [activeSection]);
26885
27127
  useEffect3(() => {
26886
27128
  if (activeSection !== "runs") {
@@ -26925,6 +27167,15 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26925
27167
  }
26926
27168
  return;
26927
27169
  }
27170
+ if (githubSyncInputMode) {
27171
+ if (key.escape) {
27172
+ setGithubSyncInputMode(null);
27173
+ setGithubSyncRepoInput("");
27174
+ setGithubSyncBranchInput("");
27175
+ setConnectionNotice(null);
27176
+ }
27177
+ return;
27178
+ }
26928
27179
  if (serviceAccountInputMode) {
26929
27180
  if (key.escape) {
26930
27181
  setServiceAccountInputMode(null);
@@ -27176,6 +27427,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27176
27427
  startConnection.reset();
27177
27428
  allowConnection.reset();
27178
27429
  removeConnection.reset();
27430
+ createGithubSyncBinding.reset();
27179
27431
  setPendingRemoveConnectionKey(null);
27180
27432
  setConnectionNotice("Refreshing providers\u2026");
27181
27433
  void refetchConnectionProviders().then(
@@ -27205,12 +27457,14 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27205
27457
  }
27206
27458
  if (input === "n") {
27207
27459
  setPendingRemoveConnectionKey(null);
27460
+ setGithubSyncInputMode(null);
27208
27461
  setConnectionNotice(null);
27209
27462
  setConnectionMode("providers");
27210
27463
  return;
27211
27464
  }
27212
27465
  if (input === "a") {
27213
27466
  setPendingRemoveConnectionKey(null);
27467
+ setGithubSyncInputMode(null);
27214
27468
  if (!selectedConnection) {
27215
27469
  setConnectionNotice("Select a connection before allowing it.");
27216
27470
  return;
@@ -27224,6 +27478,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27224
27478
  startConnection.reset();
27225
27479
  allowConnection.reset();
27226
27480
  removeConnection.reset();
27481
+ createGithubSyncBinding.reset();
27227
27482
  setConnectionNotice(null);
27228
27483
  allowConnection.mutate(
27229
27484
  {
@@ -27241,6 +27496,8 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27241
27496
  startConnection.reset();
27242
27497
  allowConnection.reset();
27243
27498
  removeConnection.reset();
27499
+ createGithubSyncBinding.reset();
27500
+ setGithubSyncInputMode(null);
27244
27501
  if (!selectedConnection) {
27245
27502
  setConnectionNotice("Select a connection before removing it.");
27246
27503
  return;
@@ -27270,11 +27527,17 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27270
27527
  );
27271
27528
  return;
27272
27529
  }
27530
+ if (input === "s") {
27531
+ beginGithubSyncBinding();
27532
+ return;
27533
+ }
27273
27534
  if (input === "r") {
27274
27535
  startConnection.reset();
27275
27536
  allowConnection.reset();
27276
27537
  removeConnection.reset();
27538
+ createGithubSyncBinding.reset();
27277
27539
  setPendingRemoveConnectionKey(null);
27540
+ setGithubSyncInputMode(null);
27278
27541
  setConnectionNotice("Refreshing connections\u2026");
27279
27542
  void refetchConnections().then(
27280
27543
  () => setConnectionNotice("Connections refreshed.")
@@ -27375,7 +27638,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27375
27638
  }
27376
27639
  const runError = errorMessage2(triggerRun.error) ?? errorMessage2(archiveRuns.error) ?? errorMessage2(unarchiveRuns.error) ?? errorMessage2(stopRuns.error);
27377
27640
  const isRunMutationPending = triggerRun.isPending || archiveRuns.isPending || unarchiveRuns.isPending || stopRuns.isPending;
27378
- const isConnectionMutationPending = startConnection.isPending || allowConnection.isPending || removeConnection.isPending;
27641
+ const isConnectionMutationPending = startConnection.isPending || allowConnection.isPending || removeConnection.isPending || createGithubSyncBinding.isPending;
27379
27642
  const serviceAccountStatus = createServiceAccount2.error instanceof Error ? createServiceAccount2.error.message : rotateServiceAccount2.error instanceof Error ? rotateServiceAccount2.error.message : removeServiceAccount2.error instanceof Error ? removeServiceAccount2.error.message : serviceAccountNotice;
27380
27643
  const isServiceAccountMutationPending = createServiceAccount2.isPending || rotateServiceAccount2.isPending || removeServiceAccount2.isPending;
27381
27644
  return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", height: termHeight, children: [
@@ -27459,6 +27722,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27459
27722
  ...connectionMode === "providers" ? [{ k: "enter", label: "Connect" }] : [
27460
27723
  { k: "n", label: "New provider" },
27461
27724
  { k: "a", label: "Allow project" },
27725
+ { k: "s", label: "Enable Sync" },
27462
27726
  { k: "d", label: "Remove" }
27463
27727
  ],
27464
27728
  { k: "r", label: "Refresh" }
@@ -27581,16 +27845,38 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27581
27845
  }
27582
27846
  )
27583
27847
  ] }) : activeSection === "connections" ? connectionMode === "providers" && isConnectionProvidersLoading ? /* @__PURE__ */ jsx14(Box13, { padding: 2, children: /* @__PURE__ */ jsx14(Spinner, { label: "loading providers\u2026" }) }) : connectionMode === "connections" && isConnectionsLoading ? /* @__PURE__ */ jsx14(Box13, { padding: 2, children: /* @__PURE__ */ jsx14(Spinner, { label: "loading connections\u2026" }) }) : /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", flexGrow: 1, children: [
27848
+ githubSyncInputMode === "repo" && /* @__PURE__ */ jsxs12(Box13, { paddingX: 2, gap: 1, children: [
27849
+ /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: "Repo:" }),
27850
+ /* @__PURE__ */ jsx14(
27851
+ TextInput3,
27852
+ {
27853
+ value: githubSyncRepoInput,
27854
+ onChange: setGithubSyncRepoInput,
27855
+ onSubmit: submitGithubSyncRepo
27856
+ }
27857
+ )
27858
+ ] }),
27859
+ githubSyncInputMode === "branch" && /* @__PURE__ */ jsxs12(Box13, { paddingX: 2, gap: 1, children: [
27860
+ /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: "Branch:" }),
27861
+ /* @__PURE__ */ jsx14(
27862
+ TextInput3,
27863
+ {
27864
+ value: githubSyncBranchInput,
27865
+ onChange: setGithubSyncBranchInput,
27866
+ onSubmit: submitGithubSyncBranch
27867
+ }
27868
+ )
27869
+ ] }),
27584
27870
  isConnectionMutationPending && /* @__PURE__ */ jsx14(Box13, { paddingX: 2, children: /* @__PURE__ */ jsx14(
27585
27871
  Spinner,
27586
27872
  {
27587
- label: startConnection.isPending ? "starting connection\u2026" : allowConnection.isPending ? "allowing project\u2026" : "removing connection\u2026"
27873
+ label: startConnection.isPending ? "starting connection\u2026" : allowConnection.isPending ? "allowing project\u2026" : removeConnection.isPending ? "removing connection\u2026" : "enabling sync\u2026"
27588
27874
  }
27589
27875
  ) }),
27590
27876
  connectionStatus && !isConnectionMutationPending && /* @__PURE__ */ jsx14(Box13, { paddingX: 2, children: /* @__PURE__ */ jsx14(
27591
27877
  Text14,
27592
27878
  {
27593
- color: startConnection.error || allowConnection.error || removeConnection.error ? "red" : "green",
27879
+ color: startConnection.error || allowConnection.error || removeConnection.error || createGithubSyncBinding.error ? "red" : "green",
27594
27880
  children: connectionStatus
27595
27881
  }
27596
27882
  ) }),
@@ -27605,6 +27891,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27605
27891
  ConnectionsView,
27606
27892
  {
27607
27893
  connections,
27894
+ syncBindings: githubSyncBindings,
27608
27895
  selectedIndex: connectionIndex,
27609
27896
  activeProjectId: activeSelectionIds.projectId,
27610
27897
  error: connectionsError instanceof Error ? connectionsError.message : connectionsError ? String(connectionsError) : null
@@ -33887,6 +34174,134 @@ function withApiBaseUrl2(context, commandOptions) {
33887
34174
  };
33888
34175
  }
33889
34176
 
34177
+ // src/commands/sync/repo.ts
34178
+ import { execFile as execFileWithCallback } from "child_process";
34179
+ import { promisify } from "util";
34180
+ var execFile = promisify(execFileWithCallback);
34181
+ async function inferGithubRepoFromOrigin() {
34182
+ try {
34183
+ const { stdout } = await execFile("git", ["remote", "get-url", "origin"], {
34184
+ cwd: process.cwd()
34185
+ });
34186
+ return parseGithubRemote(stdout.trim());
34187
+ } catch {
34188
+ return void 0;
34189
+ }
34190
+ }
34191
+ function parseGithubRemote(remote) {
34192
+ const trimmed = remote.trim();
34193
+ const ssh = /^git@github\.com:([^/\s]+)\/(.+?)(?:\.git)?$/i.exec(trimmed);
34194
+ if (ssh) {
34195
+ return `${ssh[1]}/${stripGitSuffix(ssh[2])}`;
34196
+ }
34197
+ let url2;
34198
+ try {
34199
+ url2 = new URL(trimmed.replace(/^git\+/, ""));
34200
+ } catch {
34201
+ return void 0;
34202
+ }
34203
+ if (url2.hostname.toLowerCase() !== "github.com") {
34204
+ return void 0;
34205
+ }
34206
+ const [owner, repo] = url2.pathname.replace(/^\/+/, "").split("/");
34207
+ if (!owner || !repo) {
34208
+ return void 0;
34209
+ }
34210
+ return `${owner}/${stripGitSuffix(repo)}`;
34211
+ }
34212
+ function stripGitSuffix(repo) {
34213
+ return repo.replace(/\.git$/i, "");
34214
+ }
34215
+
34216
+ // src/commands/sync/actions.ts
34217
+ async function enableGithubSyncAction(context, repoArgument, options) {
34218
+ const project = await requireProjectScope({
34219
+ context,
34220
+ options,
34221
+ explicitHint: "an active project"
34222
+ });
34223
+ const scopedContext = withProjectScope(context, project);
34224
+ const connection = await requireConnection({
34225
+ context: scopedContext,
34226
+ options,
34227
+ provider: "github",
34228
+ identifier: options.connection
34229
+ });
34230
+ const repo = repoArgument ?? await inferGithubRepoFromOrigin();
34231
+ if (!repo) {
34232
+ throw new Error(
34233
+ "No GitHub repository was provided and origin could not be inferred. Pass owner/repo."
34234
+ );
34235
+ }
34236
+ await confirmDestructiveAction(context, {
34237
+ message: `This will enable Sync for ${repo} and automatically apply .auto changes merged to ${options.branch ?? "the repository default branch"}.`,
34238
+ yes: options.yes
34239
+ });
34240
+ const client = createContextApiClient(scopedContext);
34241
+ const result = await client.createGithubSyncBinding(
34242
+ {
34243
+ connection: connection.name,
34244
+ repo,
34245
+ ...options.branch ? { branch: options.branch } : {}
34246
+ },
34247
+ { apiBaseUrl: apiUrlFromOptions(context, options) }
34248
+ );
34249
+ const style = context.io.style;
34250
+ context.writeOutput(
34251
+ style.success(
34252
+ `${result.created ? "enabled" : "updated"} sync ${result.binding.repoFullName}`
34253
+ )
34254
+ );
34255
+ context.writeOutput(
34256
+ `${style.label("connection")} ${result.binding.providerGrantId}`
34257
+ );
34258
+ context.writeOutput(
34259
+ `${style.label("branch")} ${result.binding.productionBranch}`
34260
+ );
34261
+ context.writeOutput(`${style.label("path")} ${result.binding.autoPath}`);
34262
+ }
34263
+ async function listSyncBindingsAction(context, options) {
34264
+ const project = await requireProjectScope({
34265
+ context,
34266
+ options,
34267
+ explicitHint: "an active project"
34268
+ });
34269
+ const scopedContext = withProjectScope(context, project);
34270
+ const result = await createContextApiClient(
34271
+ scopedContext
34272
+ ).listGithubSyncBindings({
34273
+ apiBaseUrl: apiUrlFromOptions(context, options)
34274
+ });
34275
+ if (result.bindings.length === 0) {
34276
+ context.writeOutput("no sync bindings found");
34277
+ return;
34278
+ }
34279
+ for (const binding of result.bindings) {
34280
+ context.writeOutput(
34281
+ [
34282
+ binding.repoFullName,
34283
+ `branch=${binding.productionBranch}`,
34284
+ `path=${binding.autoPath}`,
34285
+ `lastApplied=${binding.lastAppliedSha ?? "-"}`
34286
+ ].join(" ")
34287
+ );
34288
+ }
34289
+ }
34290
+
34291
+ // src/commands/sync/commands.ts
34292
+ function registerSyncCommands(program, context) {
34293
+ const sync = program.command("sync").description("Manage repository Sync bindings.");
34294
+ const enable = sync.command("enable").description("Enable Sync.");
34295
+ enable.command("github").description("Enable Sync for a GitHub repository.").argument("[repo]", "GitHub repository in owner/name form").option("--connection <name>", "GitHub connection name or id").option("--branch <branch>", "production branch to apply from").option("-y, --yes", "skip confirmation prompt").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(
34296
+ async (repo, commandOptions) => {
34297
+ await enableGithubSyncAction(context, repo, commandOptions);
34298
+ }
34299
+ );
34300
+ sync.command("list").alias("ls").description("List enabled Sync bindings for the active project.").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (commandOptions) => {
34301
+ await listSyncBindingsAction(context, commandOptions);
34302
+ });
34303
+ }
34304
+
33890
34305
  // src/cli/program.ts
33891
34306
  init_path();
33892
34307
  init_profiles();
@@ -34054,6 +34469,7 @@ function createProgram(options = {}) {
34054
34469
  registerProjectCommands(program, context);
34055
34470
  registerConnectionCommands(program, context);
34056
34471
  registerSecretCommands(program, context);
34472
+ registerSyncCommands(program, context);
34057
34473
  registerAgentCommands(program, context);
34058
34474
  registerRunCommands(program, context);
34059
34475
  return program;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autohq/cli",
3
- "version": "0.1.143",
3
+ "version": "0.1.144",
4
4
  "license": "SEE LICENSE IN README.md",
5
5
  "publishConfig": {
6
6
  "access": "public"