attio 0.0.1-experimental.20250409 → 0.0.1-experimental.20250425
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/api.js +2 -2
- package/lib/api/fetcher.js +2 -2
- package/lib/auth/auth.js +21 -14
- package/lib/auth/keychain.js +23 -12
- package/lib/commands/build.js +6 -0
- package/lib/commands/dev/boot.js +2 -2
- package/lib/commands/dev/graphql-code-gen.js +46 -20
- package/lib/commands/dev/onboarding.js +3 -3
- package/lib/commands/dev/upload.js +3 -3
- package/lib/commands/dev.js +12 -2
- package/lib/commands/login.js +2 -2
- package/lib/commands/logout.js +2 -2
- package/lib/commands/version/create.js +3 -3
- package/lib/commands/whoami.js +4 -4
- package/lib/spinners/determine-workspace.spinner.js +2 -2
- package/lib/spinners/get-app-info.spinner.js +2 -2
- package/lib/spinners/get-versions.spinner.js +2 -2
- package/package.json +1 -1
package/lib/api/api.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Fetcher } from "./fetcher.js";
|
|
|
2
2
|
import { APP } from "../env.js";
|
|
3
3
|
import { isErrored, complete, errored } from "@attio/fetchable";
|
|
4
4
|
import { whoamiSchema, listDevWorkspacesResponseSchema, createVersionSchema, appInfoSchema, completeBundleUploadSchema, startUploadSchema, createDevVersionSchema, installationSchema, versionsSchema, TEST_WORKSPACES, TEST_APP_INFO, tokenResponseSchema, } from "./schemas.js";
|
|
5
|
-
class
|
|
5
|
+
class ApiImpl {
|
|
6
6
|
_fetcher;
|
|
7
7
|
constructor() {
|
|
8
8
|
this._fetcher = new Fetcher();
|
|
@@ -163,4 +163,4 @@ class CliApi {
|
|
|
163
163
|
return result;
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
-
export const
|
|
166
|
+
export const api = new ApiImpl();
|
package/lib/api/fetcher.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { complete, errored, isErrored } from "@attio/fetchable";
|
|
2
2
|
import { API } from "../env.js";
|
|
3
|
-
import {
|
|
3
|
+
import { authenticator } from "../auth/auth.js";
|
|
4
4
|
export class Fetcher {
|
|
5
5
|
async _fetch({ path, fetchOptions, schema, authenticated, }) {
|
|
6
|
-
const tokenResult = authenticated === "Authenticated" ? await
|
|
6
|
+
const tokenResult = authenticated === "Authenticated" ? await authenticator.ensureAuthed() : complete(null);
|
|
7
7
|
if (isErrored(tokenResult)) {
|
|
8
8
|
return errored({
|
|
9
9
|
code: "UNAUTHORIZED",
|
package/lib/auth/auth.js
CHANGED
|
@@ -4,20 +4,20 @@ import { randomBytes, createHash } from "crypto";
|
|
|
4
4
|
import open from "open";
|
|
5
5
|
import { APP } from "../env.js";
|
|
6
6
|
import { findAvailablePort } from "../util/find-available-port.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { keychain } from "./keychain.js";
|
|
8
|
+
import { api } from "../api/api.js";
|
|
9
9
|
import { isErrored, complete, errored } from "@attio/fetchable";
|
|
10
10
|
import { printFetcherError, printKeychainError } from "../print-errors.js";
|
|
11
11
|
class AuthenticatorImpl {
|
|
12
12
|
clientId = "f881c6f1-82d7-48a5-a581-649596167845";
|
|
13
13
|
refreshTimeout = null;
|
|
14
14
|
async ensureAuthed() {
|
|
15
|
-
const existingTokenResult = await
|
|
15
|
+
const existingTokenResult = await keychain.load();
|
|
16
16
|
if (isErrored(existingTokenResult)) {
|
|
17
17
|
return this.promptToAuthenticate();
|
|
18
18
|
}
|
|
19
19
|
const existingToken = existingTokenResult.value;
|
|
20
|
-
if (existingToken === null || existingToken.
|
|
20
|
+
if (existingToken === null || existingToken.expires_at < Date.now()) {
|
|
21
21
|
return this.promptToAuthenticate();
|
|
22
22
|
}
|
|
23
23
|
this.scheduleRefresh(existingToken);
|
|
@@ -31,12 +31,12 @@ class AuthenticatorImpl {
|
|
|
31
31
|
return this.authenticate();
|
|
32
32
|
}
|
|
33
33
|
async authenticate() {
|
|
34
|
-
const existingTokenResult = await
|
|
34
|
+
const existingTokenResult = await keychain.load();
|
|
35
35
|
if (isErrored(existingTokenResult)) {
|
|
36
36
|
return existingTokenResult;
|
|
37
37
|
}
|
|
38
38
|
const existingToken = existingTokenResult.value;
|
|
39
|
-
if (existingToken !== null && existingToken.
|
|
39
|
+
if (existingToken !== null && existingToken.expires_at > Date.now()) {
|
|
40
40
|
return complete(existingToken.access_token);
|
|
41
41
|
}
|
|
42
42
|
const verifier = randomBytes(32);
|
|
@@ -64,7 +64,7 @@ class AuthenticatorImpl {
|
|
|
64
64
|
if (!receivedCode) {
|
|
65
65
|
resolveAsyncResult(errored({ code: "NO_AUTHORIZATION_CODE" }));
|
|
66
66
|
}
|
|
67
|
-
const tokenResult = await
|
|
67
|
+
const tokenResult = await api.exchangeToken({
|
|
68
68
|
receivedCode,
|
|
69
69
|
verifierString,
|
|
70
70
|
redirectUri,
|
|
@@ -106,9 +106,9 @@ class AuthenticatorImpl {
|
|
|
106
106
|
access_token: token.access_token,
|
|
107
107
|
refresh_token: token.refresh_token,
|
|
108
108
|
token_type: token.token_type,
|
|
109
|
-
|
|
109
|
+
expires_at: Date.now() + token.expires_in * 1000,
|
|
110
110
|
};
|
|
111
|
-
const saveResult = await
|
|
111
|
+
const saveResult = await keychain.save(keychainToken);
|
|
112
112
|
if (isErrored(saveResult)) {
|
|
113
113
|
return saveResult;
|
|
114
114
|
}
|
|
@@ -123,10 +123,10 @@ class AuthenticatorImpl {
|
|
|
123
123
|
if (this.refreshTimeout !== null) {
|
|
124
124
|
clearTimeout(this.refreshTimeout);
|
|
125
125
|
}
|
|
126
|
-
this.refreshTimeout = setTimeout(async () => await this.refreshToken(token), Math.max(0, token.
|
|
126
|
+
this.refreshTimeout = setTimeout(async () => await this.refreshToken(token), Math.max(0, token.expires_at - Date.now() - 5_000));
|
|
127
127
|
}
|
|
128
128
|
async refreshToken(token) {
|
|
129
|
-
const refreshTokenResult = await
|
|
129
|
+
const refreshTokenResult = await api.refreshToken({
|
|
130
130
|
refreshToken: token.refresh_token,
|
|
131
131
|
clientId: this.clientId,
|
|
132
132
|
});
|
|
@@ -139,14 +139,21 @@ class AuthenticatorImpl {
|
|
|
139
139
|
access_token: refreshedToken.access_token,
|
|
140
140
|
refresh_token: refreshedToken.refresh_token,
|
|
141
141
|
token_type: refreshedToken.token_type,
|
|
142
|
-
|
|
142
|
+
expires_at: Date.now() + refreshedToken.expires_in * 1000,
|
|
143
143
|
};
|
|
144
|
-
const saveResult = await
|
|
144
|
+
const saveResult = await keychain.save(keychainToken);
|
|
145
145
|
if (isErrored(saveResult)) {
|
|
146
146
|
printKeychainError(saveResult.error);
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
this.scheduleRefresh(keychainToken);
|
|
150
150
|
}
|
|
151
|
+
async logout() {
|
|
152
|
+
if (this.refreshTimeout !== null) {
|
|
153
|
+
clearTimeout(this.refreshTimeout);
|
|
154
|
+
this.refreshTimeout = null;
|
|
155
|
+
}
|
|
156
|
+
await keychain.delete();
|
|
157
|
+
}
|
|
151
158
|
}
|
|
152
|
-
export const
|
|
159
|
+
export const authenticator = new AuthenticatorImpl();
|
package/lib/auth/keychain.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { complete, errored } from "@attio/fetchable";
|
|
1
|
+
import { complete, errored, isErrored } from "@attio/fetchable";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
const authTokenSchema = z.object({
|
|
4
4
|
access_token: z.string(),
|
|
5
5
|
refresh_token: z.string(),
|
|
6
6
|
token_type: z.literal("Bearer"),
|
|
7
|
-
|
|
7
|
+
expires_at: z.number(),
|
|
8
8
|
});
|
|
9
9
|
class KeytarKeychain {
|
|
10
10
|
SERVICE_NAME = "attio-cli";
|
|
@@ -43,17 +43,28 @@ class KeytarKeychain {
|
|
|
43
43
|
jsonToken = JSON.parse(unparsedToken);
|
|
44
44
|
}
|
|
45
45
|
catch (error) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
error
|
|
49
|
-
}
|
|
46
|
+
const deleteResult = await this.delete();
|
|
47
|
+
if (isErrored(deleteResult)) {
|
|
48
|
+
return errored({ code: "LOAD_KEYCHAIN_ERROR", error: deleteResult.error });
|
|
49
|
+
}
|
|
50
|
+
console.debug("Wiped keychain entry due to invalid JSON");
|
|
51
|
+
return complete(null);
|
|
50
52
|
}
|
|
51
53
|
const parsedToken = authTokenSchema.safeParse(jsonToken);
|
|
52
54
|
if (!parsedToken.success) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
error:
|
|
56
|
-
}
|
|
55
|
+
const deleteResult = await this.delete();
|
|
56
|
+
if (isErrored(deleteResult)) {
|
|
57
|
+
return errored({ code: "LOAD_KEYCHAIN_ERROR", error: deleteResult.error });
|
|
58
|
+
}
|
|
59
|
+
console.debug("Wiped keychain entry due to schema mismatch");
|
|
60
|
+
return complete(null);
|
|
61
|
+
}
|
|
62
|
+
if (parsedToken.data.expires_at < Date.now() + 60_000 * 5) {
|
|
63
|
+
const deleteResult = await this.delete();
|
|
64
|
+
if (isErrored(deleteResult)) {
|
|
65
|
+
return errored({ code: "LOAD_KEYCHAIN_ERROR", error: deleteResult.error });
|
|
66
|
+
}
|
|
67
|
+
return complete(null);
|
|
57
68
|
}
|
|
58
69
|
return complete(parsedToken.data);
|
|
59
70
|
}
|
|
@@ -76,7 +87,7 @@ class TestKeychain {
|
|
|
76
87
|
access_token: "TEST",
|
|
77
88
|
refresh_token: "TEST",
|
|
78
89
|
token_type: "Bearer",
|
|
79
|
-
|
|
90
|
+
expires_at: Number.MAX_SAFE_INTEGER,
|
|
80
91
|
};
|
|
81
92
|
async save(_token) {
|
|
82
93
|
return complete(undefined);
|
|
@@ -88,4 +99,4 @@ class TestKeychain {
|
|
|
88
99
|
return complete(undefined);
|
|
89
100
|
}
|
|
90
101
|
}
|
|
91
|
-
export const
|
|
102
|
+
export const keychain = process.env.NODE_ENV === "test" ? new TestKeychain() : new KeytarKeychain();
|
package/lib/commands/build.js
CHANGED
|
@@ -5,9 +5,15 @@ import { spinnerify } from "../util/spinner.js";
|
|
|
5
5
|
import { isErrored } from "@attio/fetchable";
|
|
6
6
|
import { printJsError, printTsError } from "../util/typescript.js";
|
|
7
7
|
import { printBuildContextError } from "./dev/prepare-build-contexts.js";
|
|
8
|
+
import { graphqlCodeGen } from "./dev/graphql-code-gen.js";
|
|
9
|
+
import { hardExit } from "../util/hard-exit.js";
|
|
8
10
|
export const build = new Command("build")
|
|
9
11
|
.description("Build your Attio extension locally")
|
|
10
12
|
.action(async () => {
|
|
13
|
+
const generateGraphqlOperationsResult = await spinnerify("Generating GraphQL types...", "GraphQL types generated successfully", graphqlCodeGen);
|
|
14
|
+
if (isErrored(generateGraphqlOperationsResult)) {
|
|
15
|
+
hardExit(generateGraphqlOperationsResult.error.error.toString());
|
|
16
|
+
}
|
|
11
17
|
const tsResult = await spinnerify("Validating TypeScript...", "TypeScript validation passed", validateTypeScript);
|
|
12
18
|
if (isErrored(tsResult)) {
|
|
13
19
|
switch (tsResult.error.code) {
|
package/lib/commands/dev/boot.js
CHANGED
|
@@ -3,7 +3,7 @@ import { printFetcherError, printCliVersionError, printPackageJsonError, printDe
|
|
|
3
3
|
import { getAppSlugFromPackageJson } from "../../spinners/get-app-slug-from-package-json.js";
|
|
4
4
|
import { determineWorkspace } from "../../spinners/determine-workspace.spinner.js";
|
|
5
5
|
import { loadAttioCliVersion } from "../../util/load-attio-cli-version.js";
|
|
6
|
-
import {
|
|
6
|
+
import { api } from "../../api/api.js";
|
|
7
7
|
import { getAppInfo } from "../../spinners/get-app-info.spinner.js";
|
|
8
8
|
import { loadEnv } from "../../util/load-env.js";
|
|
9
9
|
export async function boot({ workspaceSlug }) {
|
|
@@ -32,7 +32,7 @@ export async function boot({ workspaceSlug }) {
|
|
|
32
32
|
process.exit(1);
|
|
33
33
|
}
|
|
34
34
|
const cliVersion = cliVersionResult.value;
|
|
35
|
-
const devVersionResult = await
|
|
35
|
+
const devVersionResult = await api.createDevVersion({
|
|
36
36
|
appId: appInfo.app_id,
|
|
37
37
|
cliVersion,
|
|
38
38
|
targetWorkspaceId: workspace.workspace_id,
|
|
@@ -1,39 +1,65 @@
|
|
|
1
1
|
import chokidar from "chokidar";
|
|
2
2
|
import { readFileSync } from "fs";
|
|
3
3
|
import { parse } from "graphql";
|
|
4
|
+
import { complete, errored, isErrored } from "@attio/fetchable";
|
|
4
5
|
import { findNodeModulesPath } from "../../util/find-node-modules-path.js";
|
|
5
6
|
import { generateOperations } from "../../graphql/generate-operations.js";
|
|
6
7
|
import { GraphQLError } from "../../graphql/graphql-error.js";
|
|
7
8
|
import { hardExit } from "../../util/hard-exit.js";
|
|
8
|
-
export function graphqlCodeGen(
|
|
9
|
+
export async function graphqlCodeGen() {
|
|
10
|
+
try {
|
|
11
|
+
const schemaPath = await findNodeModulesPath(["schema.graphql"]);
|
|
12
|
+
if (!schemaPath) {
|
|
13
|
+
hardExit("No schema.graphql found in node_modules");
|
|
14
|
+
}
|
|
15
|
+
const schema = parse(readFileSync(schemaPath, "utf8"));
|
|
16
|
+
await generateOperations(".", schema);
|
|
17
|
+
return complete(true);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (error instanceof GraphQLError) {
|
|
21
|
+
return errored({ code: "GRAPHQL_ERROR", error: error });
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
return errored({
|
|
25
|
+
code: "GRAPHQL_CODEGEN_ERROR",
|
|
26
|
+
error: error instanceof Error ? error.message : "GraphQL code generation failed",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function watchGraphqlCodegen(onSuccess, onError) {
|
|
9
32
|
const watcher = chokidar.watch(["**/*.graphql", "**/*.gql"], {
|
|
10
33
|
ignored: ["**/node_modules/**", "**/dist/**"],
|
|
11
34
|
cwd: ".",
|
|
12
35
|
});
|
|
13
|
-
async
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
const schema = parse(readFileSync(schemaPath, "utf8"));
|
|
20
|
-
await generateOperations(".", schema);
|
|
21
|
-
onSuccess?.();
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
if (error instanceof GraphQLError) {
|
|
25
|
-
process.stderr.write(error.toString());
|
|
36
|
+
watcher.on("ready", async () => {
|
|
37
|
+
const result = await graphqlCodeGen();
|
|
38
|
+
if (isErrored(result)) {
|
|
39
|
+
if (result.error.code === "GRAPHQL_ERROR") {
|
|
40
|
+
onError?.(result.error.error.toString());
|
|
26
41
|
}
|
|
27
42
|
else {
|
|
28
|
-
hardExit(error
|
|
43
|
+
hardExit(result.error.error.toString());
|
|
29
44
|
}
|
|
30
45
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
watcher.on("all", (event, path) => {
|
|
46
|
+
else {
|
|
47
|
+
onSuccess?.();
|
|
48
|
+
}
|
|
49
|
+
watcher.on("all", async (event, path) => {
|
|
35
50
|
if (event === "add" || event === "change" || event === "unlink") {
|
|
36
|
-
|
|
51
|
+
const result = await graphqlCodeGen();
|
|
52
|
+
if (isErrored(result)) {
|
|
53
|
+
if (result.error.code === "GRAPHQL_ERROR") {
|
|
54
|
+
onError?.(result.error.error.toString());
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
hardExit(result.error.error.toString());
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
onSuccess?.();
|
|
62
|
+
}
|
|
37
63
|
}
|
|
38
64
|
});
|
|
39
65
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { listenForKey } from "../../util/listen-for-key.js";
|
|
2
2
|
import open from "open";
|
|
3
3
|
import { APP } from "../../env.js";
|
|
4
|
-
import {
|
|
4
|
+
import { api } from "../../api/api.js";
|
|
5
5
|
import { isErrored } from "@attio/fetchable";
|
|
6
6
|
function prompt() {
|
|
7
7
|
process.stdout.write(`🚨 IMPORTANT: You will need to install your app in your workspace. Press "i" to open the app settings page, and then click "Install".\n\n`);
|
|
@@ -11,7 +11,7 @@ export function onboarding({ appId, appSlug, workspace, }) {
|
|
|
11
11
|
open(`${APP}/${workspace.slug}/settings/apps/${appSlug}`);
|
|
12
12
|
});
|
|
13
13
|
const poll = async () => {
|
|
14
|
-
const installationResult = await
|
|
14
|
+
const installationResult = await api.fetchInstallation({
|
|
15
15
|
appId,
|
|
16
16
|
workspaceId: workspace.workspace_id,
|
|
17
17
|
});
|
|
@@ -23,7 +23,7 @@ export function onboarding({ appId, appSlug, workspace, }) {
|
|
|
23
23
|
prompt();
|
|
24
24
|
while (!installation) {
|
|
25
25
|
await new Promise((resolve) => setTimeout(resolve, 60_000));
|
|
26
|
-
const installationResult = await
|
|
26
|
+
const installationResult = await api.fetchInstallation({
|
|
27
27
|
appId,
|
|
28
28
|
workspaceId: workspace.workspace_id,
|
|
29
29
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import notifier from "node-notifier";
|
|
2
2
|
import { spinnerify } from "../../util/spinner.js";
|
|
3
3
|
import { uploadBundle } from "../../util/upload-bundle.js";
|
|
4
|
-
import {
|
|
4
|
+
import { api } from "../../api/api.js";
|
|
5
5
|
import { isErrored, complete, combineAsync } from "@attio/fetchable";
|
|
6
6
|
export async function upload({ contents, devVersionId, appId, }) {
|
|
7
7
|
return await spinnerify("Uploading...", () => `Upload complete at ${new Date().toLocaleTimeString()}`, async () => {
|
|
8
|
-
const startUploadResult = await
|
|
8
|
+
const startUploadResult = await api.startUpload({
|
|
9
9
|
appId,
|
|
10
10
|
devVersionId,
|
|
11
11
|
});
|
|
@@ -21,7 +21,7 @@ export async function upload({ contents, devVersionId, appId, }) {
|
|
|
21
21
|
if (isErrored(uploadResults)) {
|
|
22
22
|
return uploadResults;
|
|
23
23
|
}
|
|
24
|
-
const completeBundleUploadResult = await
|
|
24
|
+
const completeBundleUploadResult = await api.completeBundleUpload({
|
|
25
25
|
appId,
|
|
26
26
|
devVersionId,
|
|
27
27
|
bundleId,
|
package/lib/commands/dev.js
CHANGED
|
@@ -3,7 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { printMessage } from "../util/print-message.js";
|
|
5
5
|
import { validateTypeScript } from "./dev/validate-typescript.js";
|
|
6
|
-
import {
|
|
6
|
+
import { watchGraphqlCodegen } from "./dev/graphql-code-gen.js";
|
|
7
7
|
import { bundleJavaScript } from "./dev/bundle-javascript.js";
|
|
8
8
|
import { boot } from "./dev/boot.js";
|
|
9
9
|
import { onboarding } from "./dev/onboarding.js";
|
|
@@ -76,7 +76,17 @@ export const dev = new Command("dev")
|
|
|
76
76
|
notifyTsErrors(errors);
|
|
77
77
|
});
|
|
78
78
|
cleanupFunctions.push(cleanupTs);
|
|
79
|
-
|
|
79
|
+
let hasGraphqlCodeGenError = false;
|
|
80
|
+
const cleanupGraphqlCodeGen = watchGraphqlCodegen(() => {
|
|
81
|
+
if (hasGraphqlCodeGenError) {
|
|
82
|
+
process.stdout.write(`${chalk.green("✓")} GraphQL errors fixed\n`);
|
|
83
|
+
hasGraphqlCodeGenError = false;
|
|
84
|
+
}
|
|
85
|
+
triggerTs();
|
|
86
|
+
}, (error) => {
|
|
87
|
+
hasGraphqlCodeGenError = true;
|
|
88
|
+
process.stderr.write(error);
|
|
89
|
+
});
|
|
80
90
|
cleanupFunctions.push(cleanupGraphqlCodeGen);
|
|
81
91
|
let haveJsErrors = false;
|
|
82
92
|
const cleanupJs = bundleJavaScript(async (contents) => {
|
package/lib/commands/login.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import {
|
|
2
|
+
import { authenticator } from "../auth/auth.js";
|
|
3
3
|
import { isErrored } from "@attio/fetchable";
|
|
4
4
|
import { printAuthenticationError } from "../print-errors.js";
|
|
5
5
|
export const login = new Command("login")
|
|
6
6
|
.description("Authenticate with Attio")
|
|
7
7
|
.action(async () => {
|
|
8
|
-
const result = await
|
|
8
|
+
const result = await authenticator.authenticate();
|
|
9
9
|
if (isErrored(result)) {
|
|
10
10
|
printAuthenticationError(result.error);
|
|
11
11
|
process.exit(1);
|
package/lib/commands/logout.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import {
|
|
2
|
+
import { keychain } from "../auth/keychain.js";
|
|
3
3
|
import { printKeychainError } from "../print-errors.js";
|
|
4
4
|
import { isErrored } from "@attio/fetchable";
|
|
5
5
|
export const logout = new Command("logout").description("Log out from Attio").action(async () => {
|
|
6
|
-
const result = await
|
|
6
|
+
const result = await keychain.delete();
|
|
7
7
|
if (isErrored(result)) {
|
|
8
8
|
printKeychainError(result.error);
|
|
9
9
|
process.exit(1);
|
|
@@ -10,7 +10,7 @@ import { combineAsync, isErrored } from "@attio/fetchable";
|
|
|
10
10
|
import { getAppSlugFromPackageJson } from "../../spinners/get-app-slug-from-package-json.js";
|
|
11
11
|
import { bundleJavaScript } from "./create/bundle-javascript.js";
|
|
12
12
|
import { printBuildContextError } from "../dev/prepare-build-contexts.js";
|
|
13
|
-
import {
|
|
13
|
+
import { api } from "../../api/api.js";
|
|
14
14
|
import { printJsError } from "../../util/typescript.js";
|
|
15
15
|
export const versionCreate = new Command("create")
|
|
16
16
|
.description("Create a new unpublished version of your Attio app")
|
|
@@ -59,7 +59,7 @@ export const versionCreate = new Command("create")
|
|
|
59
59
|
process.exit(1);
|
|
60
60
|
}
|
|
61
61
|
const cliVersion = cliVersionResult.value;
|
|
62
|
-
const versionResult = await
|
|
62
|
+
const versionResult = await api.createVersion({
|
|
63
63
|
appId: appInfo.app_id,
|
|
64
64
|
major: versions.length === 0
|
|
65
65
|
? 1
|
|
@@ -86,7 +86,7 @@ export const versionCreate = new Command("create")
|
|
|
86
86
|
process.exit(1);
|
|
87
87
|
}
|
|
88
88
|
const version = versionResult.value;
|
|
89
|
-
const signingResult = await spinnerify("Signing bundles...", "Bundles signed", async () => await
|
|
89
|
+
const signingResult = await spinnerify("Signing bundles...", "Bundles signed", async () => await api.completeProdBundleUpload({
|
|
90
90
|
appId: appInfo.app_id,
|
|
91
91
|
major: version.major,
|
|
92
92
|
minor: version.minor,
|
package/lib/commands/whoami.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { keychain } from "../auth/keychain.js";
|
|
3
|
+
import { api } from "../api/api.js";
|
|
4
4
|
import { isErrored } from "@attio/fetchable";
|
|
5
5
|
import { printFetcherError } from "../print-errors.js";
|
|
6
6
|
export const whoami = new Command()
|
|
7
7
|
.name("whoami")
|
|
8
8
|
.description("Identify the current user")
|
|
9
9
|
.action(async () => {
|
|
10
|
-
const tokenResult = await
|
|
10
|
+
const tokenResult = await keychain.load();
|
|
11
11
|
if (isErrored(tokenResult) || tokenResult.value === null) {
|
|
12
12
|
process.stdout.write("🔒 Not logged in.\n");
|
|
13
13
|
process.exit(0);
|
|
14
14
|
}
|
|
15
|
-
const result = await
|
|
15
|
+
const result = await api.whoami();
|
|
16
16
|
if (isErrored(result)) {
|
|
17
17
|
printFetcherError("Error fetching user", result.error);
|
|
18
18
|
process.exit(1);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { select } from "@inquirer/prompts";
|
|
2
2
|
import { spinnerify } from "../util/spinner.js";
|
|
3
|
-
import {
|
|
3
|
+
import { api } from "../api/api.js";
|
|
4
4
|
import { isErrored, complete, errored } from "@attio/fetchable";
|
|
5
5
|
export async function determineWorkspace(workspaceSlug) {
|
|
6
|
-
const workspacesResult = await spinnerify("Loading workspaces...", "Workspaces loaded", async () => await
|
|
6
|
+
const workspacesResult = await spinnerify("Loading workspaces...", "Workspaces loaded", async () => await api.fetchWorkspaces());
|
|
7
7
|
if (isErrored(workspacesResult)) {
|
|
8
8
|
return workspacesResult;
|
|
9
9
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { api } from "../api/api.js";
|
|
2
2
|
import { spinnerify } from "../util/spinner.js";
|
|
3
3
|
export async function getAppInfo(appSlug) {
|
|
4
4
|
return await spinnerify("Loading app information...", (app) => `App found: ${app.title}`, async () => {
|
|
5
|
-
return await
|
|
5
|
+
return await api.fetchAppInfo(appSlug);
|
|
6
6
|
});
|
|
7
7
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spinnerify } from "../util/spinner.js";
|
|
2
|
-
import {
|
|
2
|
+
import { api } from "../api/api.js";
|
|
3
3
|
export async function getVersions(appInfo) {
|
|
4
|
-
return await spinnerify("Loading versions...", "Versions loaded", async () => await
|
|
4
|
+
return await spinnerify("Loading versions...", "Versions loaded", async () => await api.fetchVersions(appInfo.app_id));
|
|
5
5
|
}
|