attio 0.0.1-experimental.20250303 → 0.0.1-experimental.20250321

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.
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ import { APP } from "../env.js";
3
+ import { handleError } from "./handle-error.js";
4
+ import { makeHeaders } from "./make-headers.js";
5
+ const workspacesResponseSchema = z.array(z.object({
6
+ workspace: z.object({
7
+ id: z.string(),
8
+ name: z.string(),
9
+ slug: z.string(),
10
+ }),
11
+ membership: z.object({
12
+ access_level: z.enum(["admin", "member", "suspended"]),
13
+ }),
14
+ }));
15
+ export async function fetchWorkspaces({ token }) {
16
+ const response = await fetch(`${APP}/api/common/workspaces`, {
17
+ method: "GET",
18
+ headers: makeHeaders(token),
19
+ });
20
+ await handleError(response);
21
+ return workspacesResponseSchema
22
+ .parse(await response.json())
23
+ .map(({ workspace, membership }) => ({ ...workspace, ...membership }));
24
+ }
package/lib/attio.js CHANGED
@@ -3,7 +3,6 @@ import { Command } from "commander";
3
3
  import { init } from "./commands/init.js";
4
4
  import { build } from "./commands/build.js";
5
5
  import { dev } from "./commands/dev.js";
6
- import { connection } from "./commands/connection/index.js";
7
6
  import { version } from "./commands/version/index.js";
8
7
  const program = new Command();
9
8
  program
@@ -13,6 +12,5 @@ program
13
12
  .addCommand(init)
14
13
  .addCommand(build)
15
14
  .addCommand(dev)
16
- .addCommand(connection)
17
15
  .addCommand(version)
18
16
  .parse();
@@ -1,9 +1,7 @@
1
1
  import { Command } from "commander";
2
2
  import { versionCreate } from "./create.js";
3
3
  import { versionList } from "./list.js";
4
- import { versionPublish } from "./publish.js";
5
4
  export const version = new Command("version")
6
5
  .description("Manage app versions")
7
6
  .addCommand(versionCreate)
8
- .addCommand(versionList)
9
- .addCommand(versionPublish);
7
+ .addCommand(versionList);
@@ -1,13 +1,13 @@
1
1
  import chokidar from "chokidar";
2
2
  import open from "open";
3
- import { assign, setup, enqueueActions } from "xstate";
3
+ import { assign, setup, enqueueActions, fromPromise } from "xstate";
4
4
  import { APP } from "../env.js";
5
5
  import { completeBundleUpload } from "../api/complete-bundle-upload.js";
6
6
  import { createDevVersion } from "../api/create-dev-version.js";
7
7
  import { startGraphqlServer } from "../api/start-graphql-server.js";
8
8
  import { startUpload } from "../api/start-upload.js";
9
9
  import { loadAppConfigFile } from "../util/app-config.js";
10
- import { loadDeveloperConfig, loadInitialDeveloperConfig, } from "../util/load-developer-config.js";
10
+ import { loadDeveloperConfig, loadInitialDeveloperConfig, saveTargetWorkspaceToConfig, } from "../util/load-developer-config.js";
11
11
  import notifier from "node-notifier";
12
12
  import { loadEnv } from "../util/load-env.js";
13
13
  import { loadAttioCliPackageJson } from "../util/load-attio-cli-package-json.js";
@@ -23,7 +23,8 @@ import { clearTerminal } from "../util/clear-terminal.js";
23
23
  import { setTerminalTitle } from "../util/set-terminal-title.js";
24
24
  import { printInstallInstructions } from "../util/print-install-instructions.js";
25
25
  import { printLogo } from "./actions.js";
26
- import { fromCallbackWithErrorHandling } from "./actors.js";
26
+ import { askWithTypedChoices, fromCallbackWithErrorHandling } from "./actors.js";
27
+ import { fetchWorkspaces } from "../api/fetch-workspaces.js";
27
28
  process.on("SIGINT", () => {
28
29
  process.stdout.write("\x1B[?25h");
29
30
  process.exit();
@@ -40,12 +41,18 @@ export const devMachine = setup({
40
41
  guards: {
41
42
  "have dev version": ({ context }) => Boolean(context.devVersion),
42
43
  "have typescript errors": (_, params) => Boolean(params.typeScriptErrors?.length),
44
+ "have target workspace": (_, params) => Boolean(params.config?.target_workspace_id),
45
+ "have workspaces": (_, params) => Boolean(params.output?.length),
46
+ "is workspace admin": (_, params) => params.output.access_level === "admin",
47
+ "only one workspace and admin": (_, params) => params.output.length === 1 && params.output[0].access_level === "admin",
48
+ "only one workspace and not admin": (_, params) => params.output.length === 1 && params.output[0].access_level !== "admin",
43
49
  },
44
50
  actors: {
45
51
  "javascript": jsMachine,
46
52
  "typescript": tsMachine,
47
53
  "env": envMachine,
48
54
  "code-gen": codeGenMachine,
55
+ "askForWorkspace": askWithTypedChoices(),
49
56
  "loadConfig": fromCallbackWithErrorHandling(({ sendBack }) => {
50
57
  const config = loadInitialDeveloperConfig();
51
58
  if (typeof config === "string") {
@@ -98,6 +105,15 @@ export const devMachine = setup({
98
105
  }
99
106
  };
100
107
  }),
108
+ "loadWorkspaces": fromPromise(async ({ input }) => await fetchWorkspaces({ token: input.token })),
109
+ "loadWorkspaceDetails": fromPromise(async ({ input }) => {
110
+ const workspaces = await fetchWorkspaces({ token: input.config.token });
111
+ const workspace = workspaces.find((workspace) => workspace.id === input.config.target_workspace_id);
112
+ if (!workspace) {
113
+ throw new Error(`Workspace not found: ${input.config.target_workspace_id}`);
114
+ }
115
+ return workspace;
116
+ }),
101
117
  "prepareUpload": fromCallbackWithErrorHandling(({ sendBack }) => {
102
118
  const prepareUpload = async () => {
103
119
  const config = await loadDeveloperConfig();
@@ -248,8 +264,8 @@ export const devMachine = setup({
248
264
  printUploadError: (_, params) => {
249
265
  printMessage(params.uploadError.message);
250
266
  },
251
- printGenericError: (_, params) => {
252
- process.stderr.write(`${params.error.message}\n\n`);
267
+ printError: (_, params) => {
268
+ printMessage(params.error);
253
269
  },
254
270
  printLogo: (_) => {
255
271
  process.stdout.write("\x1B[?25l");
@@ -289,7 +305,7 @@ export const devMachine = setup({
289
305
  open(`http://localhost:${context.graphqlPort}/graphql`);
290
306
  },
291
307
  openInstallPage: ({ context }) => {
292
- open(`${APP}/_/settings/apps/${context.devVersion?.app_slug}`);
308
+ open(`${APP}/${context.workspace.slug}/settings/apps/${context.devVersion?.app_slug}`);
293
309
  },
294
310
  saveTypeScriptErrors: assign({
295
311
  typeScriptErrors: (_, params) => params.errors,
@@ -329,6 +345,21 @@ export const devMachine = setup({
329
345
  ? `attio dev – ${context.devVersion.app_id}`
330
346
  : `attio dev`);
331
347
  },
348
+ setTargetWorkspace: assign({
349
+ config: (_, params) => ({
350
+ ...params.config,
351
+ target_workspace_id: params.workspace.id,
352
+ }),
353
+ }),
354
+ saveTargetWorkspaceToConfig: (_, params) => {
355
+ saveTargetWorkspaceToConfig(params.workspace.id);
356
+ },
357
+ setWorkspaces: assign({
358
+ workspaces: (_, params) => params.output,
359
+ }),
360
+ setWorkspace: assign({
361
+ workspace: (_, params) => params.output,
362
+ }),
332
363
  setSuccess: assign({
333
364
  jsContents: (_, params) => params.contents,
334
365
  lastSuccessfulJavaScriptBuild: (_, params) => params.time,
@@ -610,8 +641,9 @@ export const devMachine = setup({
610
641
  "Read Config": {
611
642
  on: {
612
643
  "Initialized": {
613
- target: "Watching",
644
+ target: "Choose Target Workspace",
614
645
  actions: { type: "setConfig", params: ({ event }) => event },
646
+ reenter: true,
615
647
  },
616
648
  "Initialization Error": {
617
649
  target: "No Config",
@@ -632,15 +664,175 @@ export const devMachine = setup({
632
664
  "Auth Error": {
633
665
  type: "final",
634
666
  },
635
- "Generic Error": {
667
+ "Choose Target Workspace": {
668
+ states: {
669
+ "Need Target Workspace?": {
670
+ always: [
671
+ {
672
+ target: "#Dev Machine.Loading Workspace Details",
673
+ guard: { type: "have target workspace", params: ({ context }) => context },
674
+ reenter: true,
675
+ },
676
+ "Loading Workspaces",
677
+ ],
678
+ },
679
+ "Loading Workspaces": {
680
+ invoke: {
681
+ src: "loadWorkspaces",
682
+ input: ({ context }) => ({ token: context.config.token }),
683
+ onDone: [
684
+ {
685
+ target: "#Dev Machine.Loading Workspace Details",
686
+ guard: {
687
+ type: "only one workspace and admin",
688
+ params: ({ event }) => event,
689
+ },
690
+ reenter: true,
691
+ actions: [
692
+ {
693
+ type: "setTargetWorkspace",
694
+ params: ({ context, event }) => ({
695
+ config: context.config,
696
+ workspace: event.output[0],
697
+ }),
698
+ },
699
+ {
700
+ type: "saveTargetWorkspaceToConfig",
701
+ params: ({ event }) => ({ workspace: event.output[0] }),
702
+ },
703
+ ],
704
+ },
705
+ {
706
+ target: "#Dev Machine.Not admin of only workspace",
707
+ guard: {
708
+ type: "only one workspace and not admin",
709
+ params: ({ event }) => event,
710
+ },
711
+ reenter: true,
712
+ actions: {
713
+ type: "printError",
714
+ params: ({ event }) => ({
715
+ error: `You are not the admin of the workspace ${event.output[0].name}. Please ask an admin to make you an admin of the workspace or create your own workspace.
716
+
717
+ ${APP}/welcome/workspace-details
718
+ `,
719
+ }),
720
+ },
721
+ },
722
+ {
723
+ target: "Ask for Workspace",
724
+ guard: { type: "have workspaces", params: ({ event }) => event },
725
+ actions: { type: "setWorkspaces", params: ({ event }) => event },
726
+ },
727
+ {
728
+ target: "No Workspaces",
729
+ reenter: true,
730
+ actions: {
731
+ type: "printError",
732
+ params: () => ({
733
+ error: `You are not the admin of any workspaces. Either request permission from an existing workspace or create your own.
734
+
735
+ ${APP}/welcome/workspace-details
736
+ `,
737
+ }),
738
+ },
739
+ },
740
+ ],
741
+ onError: {
742
+ target: "Failed to load workspaces",
743
+ actions: {
744
+ type: "printError",
745
+ params: ({ event }) => ({
746
+ error: `Failed to load workspaces: ${event.error}`,
747
+ }),
748
+ },
749
+ },
750
+ },
751
+ },
752
+ "Ask for Workspace": {
753
+ invoke: {
754
+ src: "askForWorkspace",
755
+ input: ({ context }) => ({
756
+ message: "Choose a workspace",
757
+ choices: context.workspaces.map((workspace) => ({
758
+ name: workspace.name,
759
+ value: workspace,
760
+ })),
761
+ }),
762
+ onDone: [
763
+ {
764
+ target: "#Dev Machine.Loading Workspace Details",
765
+ actions: [
766
+ {
767
+ type: "setTargetWorkspace",
768
+ params: ({ context, event }) => ({
769
+ config: context.config,
770
+ workspace: event.output,
771
+ }),
772
+ },
773
+ {
774
+ type: "saveTargetWorkspaceToConfig",
775
+ params: ({ event }) => ({ workspace: event.output }),
776
+ },
777
+ ],
778
+ guard: { type: "is workspace admin", params: ({ event }) => event },
779
+ reenter: true,
780
+ },
781
+ {
782
+ target: "#Dev Machine.Require Admin Workspace",
783
+ reenter: true,
784
+ actions: {
785
+ type: "printError",
786
+ params: ({ event }) => ({
787
+ error: `You are not the admin of the workspace ${event.output.name}. Please ask an admin to make you an admin of the workspace or create your own workspace.
788
+
789
+ ${APP}/welcome/workspace-details
790
+ `,
791
+ }),
792
+ },
793
+ },
794
+ ],
795
+ },
796
+ },
797
+ "No Workspaces": {
798
+ type: "final",
799
+ },
800
+ "Failed to load workspaces": {
801
+ type: "final",
802
+ },
803
+ },
804
+ initial: "Need Target Workspace?",
805
+ },
806
+ "Require Admin Workspace": {
807
+ type: "final",
808
+ },
809
+ "Not admin of only workspace": {
636
810
  type: "final",
637
811
  },
638
- },
639
- entry: "printLogo",
640
- on: {
641
- Error: {
642
- target: ".Generic Error",
643
- actions: { type: "printGenericError", params: ({ event }) => event },
812
+ "Loading Workspace Details": {
813
+ invoke: {
814
+ src: "loadWorkspaceDetails",
815
+ input: ({ context }) => ({
816
+ config: context.config,
817
+ }),
818
+ onDone: {
819
+ target: "Watching",
820
+ actions: { type: "setWorkspace", params: ({ event }) => event },
821
+ },
822
+ onError: {
823
+ target: "Failed to load workspaces",
824
+ actions: {
825
+ type: "printError",
826
+ params: () => ({
827
+ error: "Failed to load workspace details",
828
+ }),
829
+ },
830
+ },
831
+ },
832
+ },
833
+ "Failed to load workspaces": {
834
+ type: "final",
644
835
  },
645
836
  },
837
+ entry: "printLogo",
646
838
  });
@@ -86,13 +86,18 @@ export const initMachine = setup({
86
86
  loadAppInfo: fromPromise(async ({ input: { token, developerSlug, appSlug } }) => {
87
87
  const spinner = new Spinner();
88
88
  spinner.start("Loading app information...");
89
- const appInfo = await getAppInfo({ token, developerSlug, appSlug });
90
- if (appInfo === null) {
91
- spinner.error("App not found");
92
- return null;
89
+ try {
90
+ const appInfo = await getAppInfo({ token, developerSlug, appSlug });
91
+ if (appInfo === null) {
92
+ spinner.error("App not found");
93
+ return null;
94
+ }
95
+ spinner.success(`App found: ${appInfo.title}`);
96
+ return appInfo;
97
+ }
98
+ finally {
99
+ spinner.stop();
93
100
  }
94
- spinner.success(`App found: ${appInfo.title}`);
95
- return appInfo;
96
101
  }),
97
102
  },
98
103
  actions: {
@@ -0,0 +1,16 @@
1
+ import React from "react"
2
+ import {TextBlock, useAsyncCache} from "attio/client"
3
+ import getCatFact from "./get-cat-fact.server"
4
+
5
+ export function CatFact({recordId}) {
6
+ // By passing in the recordId, the result will be cached for each recordId
7
+ const {
8
+ values: {catFact},
9
+ // ^^^^^^^– this key matches
10
+ // vvvvvvv– this key
11
+ } = useAsyncCache({catFact: [getCatFact, recordId]})
12
+ // ^^^^^^^^^^ ^^^^^^^^
13
+ // async fn parameter(s)
14
+
15
+ return <TextBlock align="center">{`"${catFact}"`}</TextBlock>
16
+ }
@@ -0,0 +1,6 @@
1
+ export default async function getCatFact(recordId) {
2
+ // We don't really need the recordId for this API, but this is how we could use a parameter
3
+ const response = await fetch(`https://catfact.ninja/fact?${recordId}`)
4
+ const data = await response.json()
5
+ return data.fact
6
+ }
@@ -1,10 +1,11 @@
1
1
  import React from "react"
2
2
  import {TextBlock} from "attio/client"
3
- import {Advice} from "./advice"
3
+ import {CatFact} from "./cat-fact"
4
4
 
5
- const Loading = () => <TextBlock>Loading advice...</TextBlock>
5
+ const Loading = () => <TextBlock>Loading cat fact...</TextBlock>
6
6
 
7
- export function HelloWorldDialog ({recordId}) {
7
+ export function HelloWorldDialog({recordId}) {
8
+ // A simple counter to demonstrate that this is just regular React code.
8
9
  const [seconds, setSeconds] = React.useState(0)
9
10
  React.useEffect(() => {
10
11
  const timeout = setTimeout(() => setSeconds(seconds + 1), 1000)
@@ -16,9 +17,9 @@ export function HelloWorldDialog ({recordId}) {
16
17
  <TextBlock align="left">
17
18
  I am a dialog. I have been open for: {seconds} second{seconds === 1 ? "" : "s"}
18
19
  </TextBlock>
19
- {/* The hook in Advice will suspend until the advice is loaded. */}
20
+ {/* The hook in CatFact will suspend until the cat fact is loaded. */}
20
21
  <React.Suspense fallback={<Loading />}>
21
- <Advice recordId={recordId} />
22
+ <CatFact recordId={recordId} />
22
23
  </React.Suspense>
23
24
  </>
24
25
  )
@@ -0,0 +1,16 @@
1
+ import React from "react"
2
+ import {TextBlock, useAsyncCache} from "attio/client"
3
+ import getCatFact from "./get-cat-fact.server"
4
+
5
+ export function CatFact({recordId}: {recordId: string}) {
6
+ // By passing in the recordId, the result will be cached for each recordId
7
+ const {
8
+ values: {catFact},
9
+ // ^^^^^^^– this key matches
10
+ // vvvvvvv– this key
11
+ } = useAsyncCache({catFact: [getCatFact, recordId]})
12
+ // ^^^^^^^^^^ ^^^^^^^^
13
+ // async fn parameter(s)
14
+
15
+ return <TextBlock align="center">{`"${catFact}"`}</TextBlock>
16
+ }
@@ -0,0 +1,6 @@
1
+ export default async function getCatFact(recordId: string): Promise<string> {
2
+ // We don't really need the recordId for this API, but this is how we could use a parameter
3
+ const response = await fetch(`https://catfact.ninja/fact?${recordId}`)
4
+ const data = await response.json()
5
+ return data.fact
6
+ }
@@ -1,10 +1,11 @@
1
1
  import React from "react"
2
2
  import {TextBlock} from "attio/client"
3
- import {Advice} from "./advice"
3
+ import {CatFact} from "./cat-fact"
4
4
 
5
- const Loading = () => <TextBlock>Loading advice...</TextBlock>
5
+ const Loading = () => <TextBlock>Loading cat fact...</TextBlock>
6
6
 
7
7
  export function HelloWorldDialog({recordId}: {recordId: string}) {
8
+ // A simple counter to demonstrate that this is just regular React code.
8
9
  const [seconds, setSeconds] = React.useState(0)
9
10
  React.useEffect(() => {
10
11
  const timeout = setTimeout(() => setSeconds(seconds + 1), 1000)
@@ -16,9 +17,9 @@ export function HelloWorldDialog({recordId}: {recordId: string}) {
16
17
  <TextBlock align="left">
17
18
  I am a dialog. I have been open for: {seconds} second{seconds === 1 ? "" : "s"}
18
19
  </TextBlock>
19
- {/* The hook in Advice will suspend until the advice is loaded. */}
20
+ {/* The hook in CatFact will suspend until the cat fact is loaded. */}
20
21
  <React.Suspense fallback={<Loading />}>
21
- <Advice recordId={recordId} />
22
+ <CatFact recordId={recordId} />
22
23
  </React.Suspense>
23
24
  </>
24
25
  )
@@ -7,7 +7,7 @@ import { isValidSlug } from "./validate-slug.js";
7
7
  export const initialDeveloperConfigSchema = z.object({
8
8
  token: z.string(),
9
9
  developer_slug: z.string(),
10
- target_workspace_id: z.string().uuid(),
10
+ target_workspace_id: z.string().uuid().optional().nullable(),
11
11
  });
12
12
  const developerConfigSchema = initialDeveloperConfigSchema.extend({
13
13
  developer_account_id: z.string(),
@@ -76,8 +76,16 @@ export async function loadDeveloperConfig() {
76
76
  developer_slug: developerSlug,
77
77
  developer_account_id: devAccount.developer_account.developer_account_id,
78
78
  developer_account_member_id: devAccount.developer_account_member.developer_account_member_id,
79
- target_workspace_id: initialDeveloperConfig.target_workspace_id,
79
+ target_workspace_id: initialDeveloperConfig.target_workspace_id ?? null,
80
80
  });
81
81
  writeFileSync(configFile.path, stringify(config));
82
82
  return config;
83
83
  }
84
+ export async function saveTargetWorkspaceToConfig(workspaceId) {
85
+ const configFile = readDeveloperConfigFile();
86
+ if (configFile === "No config file" || configFile === "Invalid config file")
87
+ return;
88
+ const config = developerConfigSchema.parse(configFile.contents);
89
+ config.target_workspace_id = workspaceId;
90
+ writeFileSync(configFile.path, stringify(config));
91
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "attio",
3
- "version": "0.0.1-experimental.20250303",
3
+ "version": "0.0.1-experimental.20250321",
4
4
  "bin": "lib/attio.js",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,15 +0,0 @@
1
- import { z } from "zod";
2
- import { API } from "../env.js";
3
- import { handleError } from "./handle-error.js";
4
- import { makeHeaders } from "./make-headers.js";
5
- const publishVersionSchema = z.object({
6
- success: z.literal(true),
7
- });
8
- export async function publishVersion({ token, devSlug, appId, major, minor, }) {
9
- const response = await fetch(`${API}/developer-accounts/${devSlug}/apps/${appId}/prod-versions/${major}/${minor}/publish`, {
10
- method: "POST",
11
- headers: makeHeaders(token),
12
- });
13
- await handleError(response);
14
- return publishVersionSchema.parse(await response.json());
15
- }
@@ -1,67 +0,0 @@
1
- import { Command, Option } from "commander";
2
- import { createActor } from "xstate";
3
- import { z } from "zod";
4
- import { addConnectionMachine } from "../../machines/add-connection-machine.js";
5
- export const optionsSchema = z.object({
6
- label: z.string().optional(),
7
- description: z.string().optional(),
8
- type: z.enum(["secret", "oauth2-code"]).optional(),
9
- authorizeUrl: z
10
- .string()
11
- .url("Invalid URL, e.g. https://authorization-server.com/authorize")
12
- .startsWith("https://", { message: "Must provide secure authorize URL (https://...)" })
13
- .optional(),
14
- accessTokenUrl: z
15
- .string()
16
- .url("Invalid URL, e.g. https://authorization-server.com/token")
17
- .startsWith("https://", { message: "Must provide secure access token URL (https://...)" })
18
- .optional(),
19
- scopes: z
20
- .string()
21
- .refine((value) => value.split(",").every((scope) => scope.trim().length > 0), {
22
- message: "Invalid OAuth scopes format. Must be a comma-delimited list of non-empty strings.",
23
- })
24
- .optional(),
25
- clientId: z.string().optional(),
26
- clientSecret: z.string().optional(),
27
- dev: z.boolean().default(false),
28
- });
29
- export const connectionAdd = new Command("add")
30
- .description("Create a new connection for your Attio app")
31
- .addOption(new Option("--label", "The label for your connection that will be displayed to users"))
32
- .addOption(new Option("--description", "A more detailed description of your connection that will be displayed to users"))
33
- .addOption(new Option("--type <type>", "The type of connection to create").choices([
34
- "secret",
35
- "oauth2-code",
36
- ]))
37
- .addOption(new Option("--authorize-url <url>", "The authorize URL for OAuth connection"))
38
- .addOption(new Option("--access-token-url <url>", "The access token URL for OAuth connection"))
39
- .addOption(new Option("--scopes <scopes>", "A comma separated list of scopes for OAuth connection"))
40
- .addOption(new Option("--client-id <id>", "The client ID for OAuth connection"))
41
- .addOption(new Option("--client-secret <secret>", "The client secret for OAuth connection"))
42
- .addOption(new Option("--dev", "Run in development mode (additional debugging info)"))
43
- .action((unparsedOptions) => {
44
- let options;
45
- try {
46
- options = optionsSchema.parse(unparsedOptions);
47
- }
48
- catch (error) {
49
- if (error instanceof z.ZodError) {
50
- process.stderr.write("\nInvalid options:\n");
51
- error.errors.forEach((err) => {
52
- process.stderr.write(`- ${err.path.map((p) => String(p).replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)).join(".")}: ${err.message}\n`);
53
- });
54
- process.exit(1);
55
- }
56
- throw error;
57
- }
58
- const actor = createActor(addConnectionMachine, {
59
- input: options,
60
- });
61
- if (options.dev) {
62
- actor.subscribe((state) => {
63
- console.log("state:", state.value);
64
- });
65
- }
66
- actor.start();
67
- });
@@ -1,9 +0,0 @@
1
- import { Command } from "commander";
2
- import { connectionAdd } from "./add.js";
3
- import { connectionList } from "./list.js";
4
- import { connectionRemove } from "./remove.js";
5
- export const connection = new Command("connection")
6
- .description("Manage app connections")
7
- .addCommand(connectionAdd)
8
- .addCommand(connectionList)
9
- .addCommand(connectionRemove);
@@ -1,20 +0,0 @@
1
- import { Command, Option } from "commander";
2
- import { createActor } from "xstate";
3
- import { z } from "zod";
4
- import { listConnectionsMachine } from "../../machines/list-connections-machine.js";
5
- export const optionsSchema = z.object({
6
- dev: z.boolean().default(false),
7
- });
8
- export const connectionList = new Command("list")
9
- .description("List all connections for the current major version of your Attio app")
10
- .addOption(new Option("--dev", "Run in development mode (additional debugging info)"))
11
- .action((unparsedOptions) => {
12
- const options = optionsSchema.parse(unparsedOptions);
13
- const actor = createActor(listConnectionsMachine);
14
- if (options.dev) {
15
- actor.subscribe((state) => {
16
- console.log("state:", state.value);
17
- });
18
- }
19
- actor.start();
20
- });
@@ -1,20 +0,0 @@
1
- import { Command, Option } from "commander";
2
- import { createActor } from "xstate";
3
- import { z } from "zod";
4
- import { removeConnectionMachine } from "../../machines/remove-connection-machine.js";
5
- export const optionsSchema = z.object({
6
- dev: z.boolean().default(false),
7
- });
8
- export const connectionRemove = new Command("remove")
9
- .description("Remove a connection from your Attio app")
10
- .addOption(new Option("--dev", "Run in development mode (additional debugging info)"))
11
- .action((unparsedOptions) => {
12
- const options = optionsSchema.parse(unparsedOptions);
13
- const actor = createActor(removeConnectionMachine);
14
- if (options.dev) {
15
- actor.subscribe((state) => {
16
- console.log("state:", state.value);
17
- });
18
- }
19
- actor.start();
20
- });