@autohq/cli 0.1.143 → 0.1.145

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.145",
26240
26278
  license: "SEE LICENSE IN README.md",
26241
26279
  publishConfig: {
26242
26280
  access: "public"
package/dist/index.js CHANGED
@@ -83,12 +83,21 @@ var init_tokens = __esm({
83
83
  // src/lib/browser.ts
84
84
  import { spawn } from "child_process";
85
85
  function openBrowser(url2) {
86
+ if (!shouldOpenBrowser(process.env)) {
87
+ return;
88
+ }
86
89
  const child = spawn(openCommand(), openArgs(url2), {
87
90
  detached: true,
88
91
  stdio: "ignore"
89
92
  });
90
93
  child.unref();
91
94
  }
95
+ function shouldOpenBrowser(env) {
96
+ if (env.AUTO_ALLOW_BROWSER_OPEN_IN_TESTS === "1") {
97
+ return true;
98
+ }
99
+ return !env.NODE_TEST_CONTEXT;
100
+ }
92
101
  function openCommand() {
93
102
  switch (process.platform) {
94
103
  case "darwin":
@@ -17328,6 +17337,52 @@ var init_connections = __esm({
17328
17337
  }
17329
17338
  });
17330
17339
 
17340
+ // ../../packages/schemas/src/github-sync.ts
17341
+ var GITHUB_SYNC_AUTO_PATH, GithubSyncRepositoryFullNameSchema, GithubSyncProductionBranchSchema, GithubSyncBindingSchema, GithubSyncBindingCreateRequestSchema, GithubSyncBindingCreateResponseSchema, GithubSyncBindingListResponseSchema;
17342
+ var init_github_sync = __esm({
17343
+ "../../packages/schemas/src/github-sync.ts"() {
17344
+ "use strict";
17345
+ init_zod();
17346
+ init_ids();
17347
+ GITHUB_SYNC_AUTO_PATH = ".auto";
17348
+ GithubSyncRepositoryFullNameSchema = external_exports.string().trim().min(1).regex(/^[^/\s]+\/[^/\s]+$/, {
17349
+ message: "repository must be in owner/name form"
17350
+ });
17351
+ GithubSyncProductionBranchSchema = external_exports.string().trim().min(1).max(255).refine((branch) => !branch.startsWith("/") && !branch.endsWith("/"), {
17352
+ message: "branch must not start or end with /"
17353
+ });
17354
+ GithubSyncBindingSchema = external_exports.object({
17355
+ id: external_exports.string().trim().min(1),
17356
+ organizationId: OrganizationIdSchema,
17357
+ projectId: ProjectIdSchema,
17358
+ providerGrantId: ProviderGrantIdSchema,
17359
+ installationId: external_exports.string().trim().min(1),
17360
+ repoFullName: GithubSyncRepositoryFullNameSchema,
17361
+ repoId: external_exports.string().trim().min(1).nullable(),
17362
+ productionBranch: GithubSyncProductionBranchSchema,
17363
+ autoPath: external_exports.literal(GITHUB_SYNC_AUTO_PATH),
17364
+ enabled: external_exports.boolean(),
17365
+ lastPlannedSha: external_exports.string().trim().min(1).nullable(),
17366
+ lastAppliedSha: external_exports.string().trim().min(1).nullable(),
17367
+ createdAt: external_exports.string().datetime(),
17368
+ updatedAt: external_exports.string().datetime()
17369
+ });
17370
+ GithubSyncBindingCreateRequestSchema = external_exports.object({
17371
+ connection: external_exports.string().trim().min(1).optional(),
17372
+ repo: GithubSyncRepositoryFullNameSchema,
17373
+ repoId: external_exports.string().trim().min(1).optional(),
17374
+ branch: GithubSyncProductionBranchSchema.optional()
17375
+ });
17376
+ GithubSyncBindingCreateResponseSchema = external_exports.object({
17377
+ binding: GithubSyncBindingSchema,
17378
+ created: external_exports.boolean()
17379
+ });
17380
+ GithubSyncBindingListResponseSchema = external_exports.object({
17381
+ bindings: external_exports.array(GithubSyncBindingSchema)
17382
+ });
17383
+ }
17384
+ });
17385
+
17331
17386
  // ../../packages/schemas/src/github-credentials.ts
17332
17387
  var CreateRunGithubCredentialRequestSchema, RunGithubCredentialResponseSchema;
17333
17388
  var init_github_credentials = __esm({
@@ -19393,6 +19448,7 @@ var init_src = __esm({
19393
19448
  init_conversation();
19394
19449
  init_conversation_reducer();
19395
19450
  init_connections();
19451
+ init_github_sync();
19396
19452
  init_github_credentials();
19397
19453
  init_github_mcp_catalog();
19398
19454
  init_identities();
@@ -20646,6 +20702,42 @@ function createApiClient(input) {
20646
20702
  }
20647
20703
  return await response.json();
20648
20704
  },
20705
+ async createGithubSyncBinding(request, options = {}) {
20706
+ const project = await activeProject();
20707
+ const response = await authenticatedFetch(
20708
+ apiUrl(
20709
+ projectApiPath(project, "/github/sync/bindings"),
20710
+ options.apiBaseUrl
20711
+ ),
20712
+ {
20713
+ method: "POST",
20714
+ headers: {
20715
+ "content-type": "application/json"
20716
+ },
20717
+ body: JSON.stringify(request)
20718
+ },
20719
+ options.apiBaseUrl
20720
+ );
20721
+ if (!response.ok) {
20722
+ throw new Error(await responseErrorMessage(response));
20723
+ }
20724
+ return GithubSyncBindingCreateResponseSchema.parse(await response.json());
20725
+ },
20726
+ async listGithubSyncBindings(options = {}) {
20727
+ const project = await activeProject();
20728
+ const response = await authenticatedFetch(
20729
+ apiUrl(
20730
+ projectApiPath(project, "/github/sync/bindings"),
20731
+ options.apiBaseUrl
20732
+ ),
20733
+ {},
20734
+ options.apiBaseUrl
20735
+ );
20736
+ if (!response.ok) {
20737
+ throw new Error(await responseErrorMessage(response));
20738
+ }
20739
+ return GithubSyncBindingListResponseSchema.parse(await response.json());
20740
+ },
20649
20741
  ...createResourceApi({
20650
20742
  activeProject,
20651
20743
  apiUrl,
@@ -21169,7 +21261,7 @@ var init_package = __esm({
21169
21261
  "package.json"() {
21170
21262
  package_default = {
21171
21263
  name: "@autohq/cli",
21172
- version: "0.1.143",
21264
+ version: "0.1.145",
21173
21265
  license: "SEE LICENSE IN README.md",
21174
21266
  publishConfig: {
21175
21267
  access: "public"
@@ -25113,11 +25205,12 @@ import { Box as Box8, Text as Text8 } from "ink";
25113
25205
  import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
25114
25206
  function ConnectionsView({
25115
25207
  connections,
25208
+ syncBindings = [],
25116
25209
  selectedIndex,
25117
25210
  activeProjectId,
25118
25211
  error: error51
25119
25212
  }) {
25120
- const rows = connectionRows2(connections, activeProjectId);
25213
+ const rows = connectionRows2(connections, activeProjectId, syncBindings);
25121
25214
  if (error51) {
25122
25215
  return /* @__PURE__ */ jsx8(Box8, { paddingX: 2, paddingTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: "red", children: error51 }) });
25123
25216
  }
@@ -25128,7 +25221,8 @@ function ConnectionsView({
25128
25221
  /* @__PURE__ */ jsx8(HeaderCell, { width: COL_GRANT, children: "CONNECTION" }),
25129
25222
  /* @__PURE__ */ jsx8(HeaderCell, { width: COL_STATUS, children: "STATUS" }),
25130
25223
  /* @__PURE__ */ jsx8(HeaderCell, { width: COL_ACTIVE, children: "PROJECT" }),
25131
- /* @__PURE__ */ jsx8(HeaderCell, { width: COL_PROJECTS, children: "ALLOWED PROJECTS" })
25224
+ /* @__PURE__ */ jsx8(HeaderCell, { width: COL_PROJECTS, children: "ALLOWED PROJECTS" }),
25225
+ /* @__PURE__ */ jsx8(HeaderCell, { width: COL_SYNC, children: "SYNC" })
25132
25226
  ] }),
25133
25227
  rows.length === 0 ? /* @__PURE__ */ jsx8(Box8, { paddingX: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "no connections found" }) }) : rows.map((row, i) => {
25134
25228
  const isSelected = i === selectedIndex;
@@ -25157,12 +25251,13 @@ function ConnectionsView({
25157
25251
  children: activeProjectId ? row.activeProjectAllowed ? "allowed" : "-" : "-"
25158
25252
  }
25159
25253
  ) }),
25160
- /* @__PURE__ */ jsx8(TableCell, { width: COL_PROJECTS, selected: isSelected, dim, children: row.projects })
25254
+ /* @__PURE__ */ jsx8(TableCell, { width: COL_PROJECTS, selected: isSelected, dim, children: row.projects }),
25255
+ /* @__PURE__ */ jsx8(TableCell, { width: COL_SYNC, selected: isSelected, dim, children: row.sync })
25161
25256
  ] }, row.key);
25162
25257
  })
25163
25258
  ] });
25164
25259
  }
25165
- function connectionRows2(connections, activeProjectId) {
25260
+ function connectionRows2(connections, activeProjectId, syncBindings = []) {
25166
25261
  return connections.flatMap(
25167
25262
  (connection) => connection.grants.map((grant) => {
25168
25263
  const grantAccess = connection.projectAccess.filter(
@@ -25171,6 +25266,7 @@ function connectionRows2(connections, activeProjectId) {
25171
25266
  const projects = [
25172
25267
  ...new Set(grantAccess.map((access) => access.projectId))
25173
25268
  ].sort();
25269
+ const sync = syncBindings.filter((binding) => binding.providerGrantId === grant.id).map((binding) => `${binding.repoFullName}@${binding.productionBranch}`).sort();
25174
25270
  return {
25175
25271
  key: `${connection.provider}-${connection.externalAccount.id}-${grant.id}`,
25176
25272
  provider: connection.provider,
@@ -25180,24 +25276,26 @@ function connectionRows2(connections, activeProjectId) {
25180
25276
  projects: projects.length > 0 ? projects.join(",") : "-",
25181
25277
  activeProjectAllowed: grantAccess.some(
25182
25278
  (access) => access.projectId === activeProjectId
25183
- )
25279
+ ),
25280
+ sync: sync.length > 0 ? sync.join(",") : "-"
25184
25281
  };
25185
25282
  })
25186
25283
  );
25187
25284
  }
25188
- function connectionListFingerprint(connections, activeProjectId) {
25189
- return connectionRows2(connections, activeProjectId).map(
25285
+ function connectionListFingerprint(connections, activeProjectId, syncBindings = []) {
25286
+ return connectionRows2(connections, activeProjectId, syncBindings).map(
25190
25287
  (row) => [
25191
25288
  row.provider,
25192
25289
  row.account,
25193
25290
  row.grant,
25194
25291
  row.status,
25195
25292
  row.projects,
25196
- row.activeProjectAllowed ? "1" : "0"
25293
+ row.activeProjectAllowed ? "1" : "0",
25294
+ row.sync
25197
25295
  ].join("")
25198
25296
  ).join("");
25199
25297
  }
25200
- var COL_PROVIDER2, COL_ACCOUNT, COL_GRANT, COL_STATUS, COL_ACTIVE, COL_PROJECTS;
25298
+ var COL_PROVIDER2, COL_ACCOUNT, COL_GRANT, COL_STATUS, COL_ACTIVE, COL_PROJECTS, COL_SYNC;
25201
25299
  var init_ConnectionsView = __esm({
25202
25300
  "src/tui/ConnectionsView.tsx"() {
25203
25301
  "use strict";
@@ -25207,7 +25305,8 @@ var init_ConnectionsView = __esm({
25207
25305
  COL_GRANT = 22;
25208
25306
  COL_STATUS = 8;
25209
25307
  COL_ACTIVE = 8;
25210
- COL_PROJECTS = 28;
25308
+ COL_PROJECTS = 22;
25309
+ COL_SYNC = 30;
25211
25310
  }
25212
25311
  });
25213
25312
 
@@ -25546,6 +25645,10 @@ function useConnectionProviders(apiUrl, enabled = true) {
25546
25645
  const client = useApiClient();
25547
25646
  return useQuery(connectionProvidersQueryOptions(client, apiUrl, enabled));
25548
25647
  }
25648
+ function useGithubSyncBindings(apiUrl, enabled = true) {
25649
+ const client = useApiClient();
25650
+ return useQuery(githubSyncBindingsQueryOptions(client, apiUrl, enabled));
25651
+ }
25549
25652
  function useCreateServiceAccount(apiUrl) {
25550
25653
  const client = useApiClient();
25551
25654
  const queryClient = useQueryClient();
@@ -25593,6 +25696,7 @@ function useSwitchProject() {
25593
25696
  ["environments"],
25594
25697
  ["service-accounts"],
25595
25698
  ["connections"],
25699
+ ["github-sync-bindings"],
25596
25700
  ["connection-providers"]
25597
25701
  ]) {
25598
25702
  void queryClient.invalidateQueries({ queryKey });
@@ -25664,6 +25768,32 @@ function useAllowConnection(apiUrl) {
25664
25768
  })
25665
25769
  });
25666
25770
  }
25771
+ function useCreateGithubSyncBinding(apiUrl) {
25772
+ const client = useApiClient();
25773
+ const queryClient = useQueryClient();
25774
+ return useMutation({
25775
+ mutationFn: ({
25776
+ connection,
25777
+ repo,
25778
+ branch
25779
+ }) => client.createGithubSyncBinding(
25780
+ {
25781
+ connection,
25782
+ repo,
25783
+ ...branch ? { branch } : {}
25784
+ },
25785
+ { apiBaseUrl: apiUrl }
25786
+ ),
25787
+ onSuccess: () => {
25788
+ void queryClient.invalidateQueries({
25789
+ queryKey: queryKeys.githubSyncBindings(apiUrl)
25790
+ });
25791
+ void queryClient.invalidateQueries({
25792
+ queryKey: queryKeys.connections(apiUrl)
25793
+ });
25794
+ }
25795
+ });
25796
+ }
25667
25797
  function useRemoveConnection(apiUrl) {
25668
25798
  const client = useApiClient();
25669
25799
  const queryClient = useQueryClient();
@@ -25854,6 +25984,17 @@ function connectionProvidersQueryOptions(client, apiUrl, enabled = true) {
25854
25984
  staleTime: Number.POSITIVE_INFINITY
25855
25985
  });
25856
25986
  }
25987
+ function githubSyncBindingsQueryOptions(client, apiUrl, enabled = true) {
25988
+ return queryOptions({
25989
+ queryKey: queryKeys.githubSyncBindings(apiUrl),
25990
+ queryFn: async () => {
25991
+ const res = await client.listGithubSyncBindings({ apiBaseUrl: apiUrl });
25992
+ return res.bindings;
25993
+ },
25994
+ enabled,
25995
+ staleTime: Number.POSITIVE_INFINITY
25996
+ });
25997
+ }
25857
25998
  function sortAgents(list) {
25858
25999
  return list.slice().sort(
25859
26000
  (a, b) => (a.spec.environment ?? "").localeCompare(b.spec.environment ?? "") || a.metadata.name.localeCompare(b.metadata.name)
@@ -25890,6 +26031,7 @@ var init_queries = __esm({
25890
26031
  projects: (apiUrl) => ["projects", apiUrl],
25891
26032
  serviceAccounts: (apiUrl) => ["service-accounts", apiUrl],
25892
26033
  connections: (apiUrl) => ["connections", apiUrl],
26034
+ githubSyncBindings: (apiUrl) => ["github-sync-bindings", apiUrl],
25893
26035
  connectionProviders: (apiUrl) => ["connection-providers", apiUrl],
25894
26036
  runs: (apiUrl, includeArchived = false) => ["runs", apiUrl, includeArchived],
25895
26037
  agentRuns: (agentName, apiUrl, includeArchived = false) => ["agent-runs", agentName, apiUrl, includeArchived]
@@ -26329,6 +26471,9 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26329
26471
  const [connectionMode, setConnectionMode] = useState3("connections");
26330
26472
  const [serviceAccountInputMode, setServiceAccountInputMode] = useState3(null);
26331
26473
  const [serviceAccountNameInput, setServiceAccountNameInput] = useState3("");
26474
+ const [githubSyncInputMode, setGithubSyncInputMode] = useState3(null);
26475
+ const [githubSyncRepoInput, setGithubSyncRepoInput] = useState3("");
26476
+ const [githubSyncBranchInput, setGithubSyncBranchInput] = useState3("");
26332
26477
  const [inspectedResource, setInspectedResource] = useState3(null);
26333
26478
  const [inspectScrollOffset, setInspectScrollOffset] = useState3(0);
26334
26479
  const localAgentAuthoringStatuses = useMemo3(
@@ -26382,10 +26527,15 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26382
26527
  apiUrl,
26383
26528
  Boolean(activeSelectionIds.organizationId)
26384
26529
  );
26530
+ const { data: githubSyncBindings = [] } = useGithubSyncBindings(
26531
+ apiUrl,
26532
+ Boolean(activeSelectionIds.projectId)
26533
+ );
26385
26534
  const switchSelection = useSwitchProject();
26386
26535
  const startConnection = useStartConnection(apiUrl);
26387
26536
  const allowConnection = useAllowConnection(apiUrl);
26388
26537
  const removeConnection = useRemoveConnection(apiUrl);
26538
+ const createGithubSyncBinding = useCreateGithubSyncBinding(apiUrl);
26389
26539
  const createServiceAccount2 = useCreateServiceAccount(apiUrl);
26390
26540
  const rotateServiceAccount2 = useRotateServiceAccount(apiUrl);
26391
26541
  const removeServiceAccount2 = useRemoveServiceAccount(apiUrl);
@@ -26430,11 +26580,13 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26430
26580
  );
26431
26581
  const selectedConnection = connectionRows2(
26432
26582
  connections,
26433
- activeSelectionIds.projectId
26583
+ activeSelectionIds.projectId,
26584
+ githubSyncBindings
26434
26585
  )[connectionIndex];
26435
26586
  const connectionFingerprint = connectionListFingerprint(
26436
26587
  connections,
26437
- activeSelectionIds.projectId
26588
+ activeSelectionIds.projectId,
26589
+ githubSyncBindings
26438
26590
  );
26439
26591
  const connectionProviderRowCount = connectionProviders.length;
26440
26592
  const selectedConnectionProvider = providerRows2(connectionProviders)[connectionProviderIndex];
@@ -26526,7 +26678,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26526
26678
  );
26527
26679
  if (index >= 0) setEnvironmentIndex(index);
26528
26680
  }, [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;
26681
+ 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
26682
  const startRun = useCallback2(() => {
26531
26683
  if (!scopedAgent || triggerRun.isPending) return;
26532
26684
  triggerRun.reset();
@@ -26736,7 +26888,8 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26736
26888
  );
26737
26889
  const nextFingerprint = connectionListFingerprint(
26738
26890
  response.connections,
26739
- activeSelectionIds.projectId
26891
+ activeSelectionIds.projectId,
26892
+ githubSyncBindings
26740
26893
  );
26741
26894
  if (nextFingerprint !== input.beforeFingerprint) {
26742
26895
  setConnectionNotice(`${input.provider} connection status updated.`);
@@ -26767,14 +26920,24 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26767
26920
  void refresh();
26768
26921
  }, CONNECTION_REFRESH_INTERVAL_MS);
26769
26922
  },
26770
- [activeSelectionIds.projectId, apiUrl, client, queryClient]
26923
+ [
26924
+ activeSelectionIds.projectId,
26925
+ apiUrl,
26926
+ client,
26927
+ githubSyncBindings,
26928
+ queryClient
26929
+ ]
26771
26930
  );
26772
26931
  const beginConnection = useCallback2(
26773
26932
  (provider) => {
26774
26933
  startConnection.reset();
26775
26934
  allowConnection.reset();
26776
26935
  removeConnection.reset();
26936
+ createGithubSyncBinding.reset();
26777
26937
  setPendingRemoveConnectionKey(null);
26938
+ setGithubSyncInputMode(null);
26939
+ setGithubSyncRepoInput("");
26940
+ setGithubSyncBranchInput("");
26778
26941
  setConnectionNotice(null);
26779
26942
  startConnection.mutate(
26780
26943
  {
@@ -26806,6 +26969,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26806
26969
  allowConnection,
26807
26970
  connectionFingerprint,
26808
26971
  removeConnection,
26972
+ createGithubSyncBinding,
26809
26973
  refreshConnectionsUntilChanged,
26810
26974
  startConnection
26811
26975
  ]
@@ -26843,6 +27007,88 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26843
27007
  serviceAccountNameInput,
26844
27008
  serviceAccounts
26845
27009
  ]);
27010
+ const beginGithubSyncBinding = useCallback2(() => {
27011
+ startConnection.reset();
27012
+ allowConnection.reset();
27013
+ removeConnection.reset();
27014
+ createGithubSyncBinding.reset();
27015
+ setPendingRemoveConnectionKey(null);
27016
+ if (!selectedConnection) {
27017
+ setConnectionNotice("Select a GitHub connection before enabling Sync.");
27018
+ return;
27019
+ }
27020
+ if (selectedConnection.provider !== "github") {
27021
+ setConnectionNotice("Sync v1 supports GitHub connections only.");
27022
+ return;
27023
+ }
27024
+ if (!activeSelectionIds.projectId) {
27025
+ setConnectionNotice("Select an active project before enabling Sync.");
27026
+ return;
27027
+ }
27028
+ if (!selectedConnection.activeProjectAllowed) {
27029
+ setConnectionNotice(
27030
+ "Allow this GitHub connection for the active project first."
27031
+ );
27032
+ return;
27033
+ }
27034
+ setConnectionNotice(null);
27035
+ setGithubSyncRepoInput("");
27036
+ setGithubSyncBranchInput("");
27037
+ setGithubSyncInputMode("repo");
27038
+ }, [
27039
+ activeSelectionIds.projectId,
27040
+ allowConnection,
27041
+ createGithubSyncBinding,
27042
+ removeConnection,
27043
+ selectedConnection,
27044
+ startConnection
27045
+ ]);
27046
+ const submitGithubSyncRepo = useCallback2(() => {
27047
+ const repo = githubSyncRepoInput.trim();
27048
+ if (!repo) {
27049
+ setConnectionNotice("Enter a GitHub repository in owner/name form.");
27050
+ return;
27051
+ }
27052
+ setConnectionNotice(null);
27053
+ setGithubSyncInputMode("branch");
27054
+ }, [githubSyncRepoInput]);
27055
+ const submitGithubSyncBranch = useCallback2(() => {
27056
+ if (!selectedConnection) {
27057
+ setGithubSyncInputMode(null);
27058
+ setConnectionNotice("Select a GitHub connection before enabling Sync.");
27059
+ return;
27060
+ }
27061
+ const repo = githubSyncRepoInput.trim();
27062
+ const branch = githubSyncBranchInput.trim();
27063
+ if (!repo) {
27064
+ setGithubSyncInputMode("repo");
27065
+ setConnectionNotice("Enter a GitHub repository in owner/name form.");
27066
+ return;
27067
+ }
27068
+ setConnectionNotice(null);
27069
+ createGithubSyncBinding.mutate(
27070
+ {
27071
+ connection: selectedConnection.grant,
27072
+ repo,
27073
+ ...branch ? { branch } : {}
27074
+ },
27075
+ {
27076
+ onSuccess: (result) => {
27077
+ setGithubSyncInputMode(null);
27078
+ setGithubSyncRepoInput("");
27079
+ setGithubSyncBranchInput("");
27080
+ setConnectionNotice(
27081
+ `${result.created ? "Enabled" : "Updated"} Sync for ${result.binding.repoFullName} on ${result.binding.productionBranch}.`
27082
+ );
27083
+ }
27084
+ }
27085
+ );
27086
+ }, [
27087
+ createGithubSyncBinding,
27088
+ githubSyncBranchInput,
27089
+ githubSyncRepoInput,
27090
+ selectedConnection
27091
+ ]);
26846
27092
  const selectTopLevelSection = useCallback2(
26847
27093
  (section) => {
26848
27094
  if (section === "agents") {
@@ -26881,6 +27127,11 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26881
27127
  setPendingRemoveServiceAccountName(null);
26882
27128
  setPendingRotateServiceAccountName(null);
26883
27129
  }
27130
+ if (activeSection !== "connections") {
27131
+ setGithubSyncInputMode(null);
27132
+ setGithubSyncRepoInput("");
27133
+ setGithubSyncBranchInput("");
27134
+ }
26884
27135
  }, [activeSection]);
26885
27136
  useEffect3(() => {
26886
27137
  if (activeSection !== "runs") {
@@ -26925,6 +27176,15 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
26925
27176
  }
26926
27177
  return;
26927
27178
  }
27179
+ if (githubSyncInputMode) {
27180
+ if (key.escape) {
27181
+ setGithubSyncInputMode(null);
27182
+ setGithubSyncRepoInput("");
27183
+ setGithubSyncBranchInput("");
27184
+ setConnectionNotice(null);
27185
+ }
27186
+ return;
27187
+ }
26928
27188
  if (serviceAccountInputMode) {
26929
27189
  if (key.escape) {
26930
27190
  setServiceAccountInputMode(null);
@@ -27176,6 +27436,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27176
27436
  startConnection.reset();
27177
27437
  allowConnection.reset();
27178
27438
  removeConnection.reset();
27439
+ createGithubSyncBinding.reset();
27179
27440
  setPendingRemoveConnectionKey(null);
27180
27441
  setConnectionNotice("Refreshing providers\u2026");
27181
27442
  void refetchConnectionProviders().then(
@@ -27205,12 +27466,14 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27205
27466
  }
27206
27467
  if (input === "n") {
27207
27468
  setPendingRemoveConnectionKey(null);
27469
+ setGithubSyncInputMode(null);
27208
27470
  setConnectionNotice(null);
27209
27471
  setConnectionMode("providers");
27210
27472
  return;
27211
27473
  }
27212
27474
  if (input === "a") {
27213
27475
  setPendingRemoveConnectionKey(null);
27476
+ setGithubSyncInputMode(null);
27214
27477
  if (!selectedConnection) {
27215
27478
  setConnectionNotice("Select a connection before allowing it.");
27216
27479
  return;
@@ -27224,6 +27487,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27224
27487
  startConnection.reset();
27225
27488
  allowConnection.reset();
27226
27489
  removeConnection.reset();
27490
+ createGithubSyncBinding.reset();
27227
27491
  setConnectionNotice(null);
27228
27492
  allowConnection.mutate(
27229
27493
  {
@@ -27241,6 +27505,8 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27241
27505
  startConnection.reset();
27242
27506
  allowConnection.reset();
27243
27507
  removeConnection.reset();
27508
+ createGithubSyncBinding.reset();
27509
+ setGithubSyncInputMode(null);
27244
27510
  if (!selectedConnection) {
27245
27511
  setConnectionNotice("Select a connection before removing it.");
27246
27512
  return;
@@ -27270,11 +27536,17 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27270
27536
  );
27271
27537
  return;
27272
27538
  }
27539
+ if (input === "s") {
27540
+ beginGithubSyncBinding();
27541
+ return;
27542
+ }
27273
27543
  if (input === "r") {
27274
27544
  startConnection.reset();
27275
27545
  allowConnection.reset();
27276
27546
  removeConnection.reset();
27547
+ createGithubSyncBinding.reset();
27277
27548
  setPendingRemoveConnectionKey(null);
27549
+ setGithubSyncInputMode(null);
27278
27550
  setConnectionNotice("Refreshing connections\u2026");
27279
27551
  void refetchConnections().then(
27280
27552
  () => setConnectionNotice("Connections refreshed.")
@@ -27375,7 +27647,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27375
27647
  }
27376
27648
  const runError = errorMessage2(triggerRun.error) ?? errorMessage2(archiveRuns.error) ?? errorMessage2(unarchiveRuns.error) ?? errorMessage2(stopRuns.error);
27377
27649
  const isRunMutationPending = triggerRun.isPending || archiveRuns.isPending || unarchiveRuns.isPending || stopRuns.isPending;
27378
- const isConnectionMutationPending = startConnection.isPending || allowConnection.isPending || removeConnection.isPending;
27650
+ const isConnectionMutationPending = startConnection.isPending || allowConnection.isPending || removeConnection.isPending || createGithubSyncBinding.isPending;
27379
27651
  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
27652
  const isServiceAccountMutationPending = createServiceAccount2.isPending || rotateServiceAccount2.isPending || removeServiceAccount2.isPending;
27381
27653
  return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", height: termHeight, children: [
@@ -27459,6 +27731,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27459
27731
  ...connectionMode === "providers" ? [{ k: "enter", label: "Connect" }] : [
27460
27732
  { k: "n", label: "New provider" },
27461
27733
  { k: "a", label: "Allow project" },
27734
+ { k: "s", label: "Enable Sync" },
27462
27735
  { k: "d", label: "Remove" }
27463
27736
  ],
27464
27737
  { k: "r", label: "Refresh" }
@@ -27581,16 +27854,38 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27581
27854
  }
27582
27855
  )
27583
27856
  ] }) : 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: [
27857
+ githubSyncInputMode === "repo" && /* @__PURE__ */ jsxs12(Box13, { paddingX: 2, gap: 1, children: [
27858
+ /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: "Repo:" }),
27859
+ /* @__PURE__ */ jsx14(
27860
+ TextInput3,
27861
+ {
27862
+ value: githubSyncRepoInput,
27863
+ onChange: setGithubSyncRepoInput,
27864
+ onSubmit: submitGithubSyncRepo
27865
+ }
27866
+ )
27867
+ ] }),
27868
+ githubSyncInputMode === "branch" && /* @__PURE__ */ jsxs12(Box13, { paddingX: 2, gap: 1, children: [
27869
+ /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: "Branch:" }),
27870
+ /* @__PURE__ */ jsx14(
27871
+ TextInput3,
27872
+ {
27873
+ value: githubSyncBranchInput,
27874
+ onChange: setGithubSyncBranchInput,
27875
+ onSubmit: submitGithubSyncBranch
27876
+ }
27877
+ )
27878
+ ] }),
27584
27879
  isConnectionMutationPending && /* @__PURE__ */ jsx14(Box13, { paddingX: 2, children: /* @__PURE__ */ jsx14(
27585
27880
  Spinner,
27586
27881
  {
27587
- label: startConnection.isPending ? "starting connection\u2026" : allowConnection.isPending ? "allowing project\u2026" : "removing connection\u2026"
27882
+ label: startConnection.isPending ? "starting connection\u2026" : allowConnection.isPending ? "allowing project\u2026" : removeConnection.isPending ? "removing connection\u2026" : "enabling sync\u2026"
27588
27883
  }
27589
27884
  ) }),
27590
27885
  connectionStatus && !isConnectionMutationPending && /* @__PURE__ */ jsx14(Box13, { paddingX: 2, children: /* @__PURE__ */ jsx14(
27591
27886
  Text14,
27592
27887
  {
27593
- color: startConnection.error || allowConnection.error || removeConnection.error ? "red" : "green",
27888
+ color: startConnection.error || allowConnection.error || removeConnection.error || createGithubSyncBinding.error ? "red" : "green",
27594
27889
  children: connectionStatus
27595
27890
  }
27596
27891
  ) }),
@@ -27605,6 +27900,7 @@ function HomeView({ apiUrl, notice, returnToAgent }) {
27605
27900
  ConnectionsView,
27606
27901
  {
27607
27902
  connections,
27903
+ syncBindings: githubSyncBindings,
27608
27904
  selectedIndex: connectionIndex,
27609
27905
  activeProjectId: activeSelectionIds.projectId,
27610
27906
  error: connectionsError instanceof Error ? connectionsError.message : connectionsError ? String(connectionsError) : null
@@ -33887,6 +34183,134 @@ function withApiBaseUrl2(context, commandOptions) {
33887
34183
  };
33888
34184
  }
33889
34185
 
34186
+ // src/commands/sync/repo.ts
34187
+ import { execFile as execFileWithCallback } from "child_process";
34188
+ import { promisify } from "util";
34189
+ var execFile = promisify(execFileWithCallback);
34190
+ async function inferGithubRepoFromOrigin() {
34191
+ try {
34192
+ const { stdout } = await execFile("git", ["remote", "get-url", "origin"], {
34193
+ cwd: process.cwd()
34194
+ });
34195
+ return parseGithubRemote(stdout.trim());
34196
+ } catch {
34197
+ return void 0;
34198
+ }
34199
+ }
34200
+ function parseGithubRemote(remote) {
34201
+ const trimmed = remote.trim();
34202
+ const ssh = /^git@github\.com:([^/\s]+)\/(.+?)(?:\.git)?$/i.exec(trimmed);
34203
+ if (ssh) {
34204
+ return `${ssh[1]}/${stripGitSuffix(ssh[2])}`;
34205
+ }
34206
+ let url2;
34207
+ try {
34208
+ url2 = new URL(trimmed.replace(/^git\+/, ""));
34209
+ } catch {
34210
+ return void 0;
34211
+ }
34212
+ if (url2.hostname.toLowerCase() !== "github.com") {
34213
+ return void 0;
34214
+ }
34215
+ const [owner, repo] = url2.pathname.replace(/^\/+/, "").split("/");
34216
+ if (!owner || !repo) {
34217
+ return void 0;
34218
+ }
34219
+ return `${owner}/${stripGitSuffix(repo)}`;
34220
+ }
34221
+ function stripGitSuffix(repo) {
34222
+ return repo.replace(/\.git$/i, "");
34223
+ }
34224
+
34225
+ // src/commands/sync/actions.ts
34226
+ async function enableGithubSyncAction(context, repoArgument, options) {
34227
+ const project = await requireProjectScope({
34228
+ context,
34229
+ options,
34230
+ explicitHint: "an active project"
34231
+ });
34232
+ const scopedContext = withProjectScope(context, project);
34233
+ const connection = await requireConnection({
34234
+ context: scopedContext,
34235
+ options,
34236
+ provider: "github",
34237
+ identifier: options.connection
34238
+ });
34239
+ const repo = repoArgument ?? await inferGithubRepoFromOrigin();
34240
+ if (!repo) {
34241
+ throw new Error(
34242
+ "No GitHub repository was provided and origin could not be inferred. Pass owner/repo."
34243
+ );
34244
+ }
34245
+ await confirmDestructiveAction(context, {
34246
+ message: `This will enable Sync for ${repo} and automatically apply .auto changes merged to ${options.branch ?? "the repository default branch"}.`,
34247
+ yes: options.yes
34248
+ });
34249
+ const client = createContextApiClient(scopedContext);
34250
+ const result = await client.createGithubSyncBinding(
34251
+ {
34252
+ connection: connection.name,
34253
+ repo,
34254
+ ...options.branch ? { branch: options.branch } : {}
34255
+ },
34256
+ { apiBaseUrl: apiUrlFromOptions(context, options) }
34257
+ );
34258
+ const style = context.io.style;
34259
+ context.writeOutput(
34260
+ style.success(
34261
+ `${result.created ? "enabled" : "updated"} sync ${result.binding.repoFullName}`
34262
+ )
34263
+ );
34264
+ context.writeOutput(
34265
+ `${style.label("connection")} ${result.binding.providerGrantId}`
34266
+ );
34267
+ context.writeOutput(
34268
+ `${style.label("branch")} ${result.binding.productionBranch}`
34269
+ );
34270
+ context.writeOutput(`${style.label("path")} ${result.binding.autoPath}`);
34271
+ }
34272
+ async function listSyncBindingsAction(context, options) {
34273
+ const project = await requireProjectScope({
34274
+ context,
34275
+ options,
34276
+ explicitHint: "an active project"
34277
+ });
34278
+ const scopedContext = withProjectScope(context, project);
34279
+ const result = await createContextApiClient(
34280
+ scopedContext
34281
+ ).listGithubSyncBindings({
34282
+ apiBaseUrl: apiUrlFromOptions(context, options)
34283
+ });
34284
+ if (result.bindings.length === 0) {
34285
+ context.writeOutput("no sync bindings found");
34286
+ return;
34287
+ }
34288
+ for (const binding of result.bindings) {
34289
+ context.writeOutput(
34290
+ [
34291
+ binding.repoFullName,
34292
+ `branch=${binding.productionBranch}`,
34293
+ `path=${binding.autoPath}`,
34294
+ `lastApplied=${binding.lastAppliedSha ?? "-"}`
34295
+ ].join(" ")
34296
+ );
34297
+ }
34298
+ }
34299
+
34300
+ // src/commands/sync/commands.ts
34301
+ function registerSyncCommands(program, context) {
34302
+ const sync = program.command("sync").description("Manage repository Sync bindings.");
34303
+ const enable = sync.command("enable").description("Enable Sync.");
34304
+ 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(
34305
+ async (repo, commandOptions) => {
34306
+ await enableGithubSyncAction(context, repo, commandOptions);
34307
+ }
34308
+ );
34309
+ 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) => {
34310
+ await listSyncBindingsAction(context, commandOptions);
34311
+ });
34312
+ }
34313
+
33890
34314
  // src/cli/program.ts
33891
34315
  init_path();
33892
34316
  init_profiles();
@@ -34054,6 +34478,7 @@ function createProgram(options = {}) {
34054
34478
  registerProjectCommands(program, context);
34055
34479
  registerConnectionCommands(program, context);
34056
34480
  registerSecretCommands(program, context);
34481
+ registerSyncCommands(program, context);
34057
34482
  registerAgentCommands(program, context);
34058
34483
  registerRunCommands(program, context);
34059
34484
  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.145",
4
4
  "license": "SEE LICENSE IN README.md",
5
5
  "publishConfig": {
6
6
  "access": "public"