attio 0.0.1-experimental.20251031.1 → 0.0.1-experimental.20251104.2
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/README.md +0 -6
- package/lib/attio.js +417840 -22
- package/lib/build/client/generate-client-entry.js +1 -1
- package/lib/build/server/generate-server-entry.js +1 -1
- package/lib/build.js +1 -1
- package/lib/client/workflow/outcome-data-schema/array-node.d.ts +1 -3
- package/lib/client/workflow/outcome-data-schema/array-node.d.ts.map +1 -1
- package/lib/client/workflow/outcome-data-schema/attio-list.d.ts +1 -3
- package/lib/client/workflow/outcome-data-schema/attio-list.d.ts.map +1 -1
- package/lib/client/workflow/outcome-data-schema/attio-object.d.ts +1 -3
- package/lib/client/workflow/outcome-data-schema/attio-object.d.ts.map +1 -1
- package/lib/client/workflow/outcome-data-schema/attio-record.d.ts +1 -3
- package/lib/client/workflow/outcome-data-schema/attio-record.d.ts.map +1 -1
- package/lib/client/workflow/outcome-data-schema/attio-select.d.ts +1 -3
- package/lib/client/workflow/outcome-data-schema/attio-select.d.ts.map +1 -1
- package/lib/client/workflow/outcome-data-schema/boolean-node.d.ts +1 -3
- package/lib/client/workflow/outcome-data-schema/boolean-node.d.ts.map +1 -1
- package/lib/client/workflow/outcome-data-schema/number-node.d.ts +1 -3
- package/lib/client/workflow/outcome-data-schema/number-node.d.ts.map +1 -1
- package/lib/client/workflow/outcome-data-schema/string-node.d.ts +1 -3
- package/lib/client/workflow/outcome-data-schema/string-node.d.ts.map +1 -1
- package/lib/client/workflow/outcome-data-schema/struct-node.d.ts +1 -3
- package/lib/client/workflow/outcome-data-schema/struct-node.d.ts.map +1 -1
- package/lib/client/workflow/outcome-data-schema/timestamp-node.d.ts +1 -3
- package/lib/client/workflow/outcome-data-schema/timestamp-node.d.ts.map +1 -1
- package/lib/commands/build.js +1 -2
- package/lib/commands/dev/boot.js +1 -2
- package/lib/commands/dev/graphql-code-gen.js +1 -1
- package/lib/commands/dev/graphql-server.js +5 -6
- package/lib/commands/dev/onboarding.js +1 -2
- package/lib/commands/dev/upload.js +1 -2
- package/lib/commands/dev.js +5 -4
- package/lib/commands/init.js +9 -41
- package/lib/commands/login.js +1 -2
- package/lib/commands/logout.js +1 -2
- package/lib/commands/logs.js +1 -3
- package/lib/commands/version/create.js +1 -4
- package/lib/commands/version/list.js +1 -2
- package/lib/commands/whoami.js +1 -3
- package/lib/server/workflow/block-config/config-schema/attio-attribute.d.ts +1 -3
- package/lib/server/workflow/block-config/config-schema/attio-attribute.d.ts.map +1 -1
- package/lib/server/workflow/block-config/config-schema/attio-list.d.ts +1 -3
- package/lib/server/workflow/block-config/config-schema/attio-list.d.ts.map +1 -1
- package/lib/server/workflow/block-config/config-schema/attio-object.d.ts +1 -3
- package/lib/server/workflow/block-config/config-schema/attio-object.d.ts.map +1 -1
- package/lib/server/workflow/config-schema/array-node.d.ts +1 -3
- package/lib/server/workflow/config-schema/array-node.d.ts.map +1 -1
- package/lib/server/workflow/config-schema/attio-attribute.d.ts +1 -3
- package/lib/server/workflow/config-schema/attio-attribute.d.ts.map +1 -1
- package/lib/server/workflow/config-schema/attio-list.d.ts +1 -3
- package/lib/server/workflow/config-schema/attio-list.d.ts.map +1 -1
- package/lib/server/workflow/config-schema/attio-object.d.ts +1 -3
- package/lib/server/workflow/config-schema/attio-object.d.ts.map +1 -1
- package/lib/server/workflow/config-schema/attio-record.d.ts +1 -3
- package/lib/server/workflow/config-schema/attio-record.d.ts.map +1 -1
- package/lib/server/workflow/config-schema/attio-select.d.ts +1 -3
- package/lib/server/workflow/config-schema/attio-select.d.ts.map +1 -1
- package/lib/server/workflow/config-schema/boolean-node.d.ts +1 -3
- package/lib/server/workflow/config-schema/boolean-node.d.ts.map +1 -1
- package/lib/server/workflow/config-schema/number-node.d.ts +1 -3
- package/lib/server/workflow/config-schema/number-node.d.ts.map +1 -1
- package/lib/server/workflow/config-schema/string-node.d.ts +1 -3
- package/lib/server/workflow/config-schema/string-node.d.ts.map +1 -1
- package/lib/server/workflow/config-schema/struct-node.d.ts +1 -3
- package/lib/server/workflow/config-schema/struct-node.d.ts.map +1 -1
- package/lib/server/workflow/config-schema/timestamp-node.d.ts +1 -3
- package/lib/server/workflow/config-schema/timestamp-node.d.ts.map +1 -1
- package/lib/shared/schema-utils/array-node.d.ts +3 -9
- package/lib/shared/schema-utils/array-node.d.ts.map +1 -1
- package/lib/shared/schema-utils/base-node.d.ts +1 -3
- package/lib/shared/schema-utils/base-node.d.ts.map +1 -1
- package/lib/shared/schema-utils/index.d.ts +3 -0
- package/lib/shared/schema-utils/index.d.ts.map +1 -1
- package/lib/shared/schema-utils/normalize.d.ts +1 -3
- package/lib/shared/schema-utils/normalize.d.ts.map +1 -1
- package/lib/shared/schema-utils/optional.d.ts +5 -0
- package/lib/shared/schema-utils/optional.d.ts.map +1 -0
- package/lib/shared/schema-utils/{attio-list.js → optional.js} +1 -1
- package/lib/shared/schema-utils/optional.js.map +1 -0
- package/lib/shared/schema-utils/path.d.ts +7 -5
- package/lib/shared/schema-utils/path.d.ts.map +1 -1
- package/lib/shared/schema-utils/resolved-path.d.ts +3 -5
- package/lib/shared/schema-utils/resolved-path.d.ts.map +1 -1
- package/lib/shared/schema-utils/struct-node.d.ts +3 -9
- package/lib/shared/schema-utils/struct-node.d.ts.map +1 -1
- package/lib/shared/schema-utils/undefined-node.d.ts +5 -0
- package/lib/shared/schema-utils/undefined-node.d.ts.map +1 -0
- package/lib/shared/schema-utils/{attio-attribute.js → undefined-node.js} +1 -1
- package/lib/shared/schema-utils/undefined-node.js.map +1 -0
- package/lib/shared/schema-utils/union-node.d.ts +8 -0
- package/lib/shared/schema-utils/union-node.d.ts.map +1 -0
- package/lib/shared/schema-utils/{attio-object.js → union-node.js} +1 -1
- package/lib/shared/schema-utils/union-node.js.map +1 -0
- package/lib/shared/workflow/activator.d.ts +10 -4
- package/lib/shared/workflow/activator.d.ts.map +1 -1
- package/lib/shared/workflow/config/attio-attribute.d.ts +3 -10
- package/lib/shared/workflow/config/attio-attribute.d.ts.map +1 -1
- package/lib/shared/workflow/config/attio-list.d.ts +3 -10
- package/lib/shared/workflow/config/attio-list.d.ts.map +1 -1
- package/lib/shared/workflow/config/attio-object.d.ts +3 -10
- package/lib/shared/workflow/config/attio-object.d.ts.map +1 -1
- package/lib/shared/workflow/config/attio-record.d.ts +3 -10
- package/lib/shared/workflow/config/attio-record.d.ts.map +1 -1
- package/lib/shared/workflow/config/attio-select.d.ts +3 -10
- package/lib/shared/workflow/config/attio-select.d.ts.map +1 -1
- package/lib/shared/workflow/config/boolean-node.d.ts +3 -10
- package/lib/shared/workflow/config/boolean-node.d.ts.map +1 -1
- package/lib/shared/workflow/config/friendly-value-of.d.ts +3 -5
- package/lib/shared/workflow/config/friendly-value-of.d.ts.map +1 -1
- package/lib/shared/workflow/config/initial-values-of.d.ts +1 -1
- package/lib/shared/workflow/config/initial-values-of.d.ts.map +1 -1
- package/lib/shared/workflow/config/node.d.ts +2 -10
- package/lib/shared/workflow/config/node.d.ts.map +1 -1
- package/lib/shared/workflow/config/number-node.d.ts +3 -10
- package/lib/shared/workflow/config/number-node.d.ts.map +1 -1
- package/lib/shared/workflow/config/rich-text-node.d.ts +3 -10
- package/lib/shared/workflow/config/rich-text-node.d.ts.map +1 -1
- package/lib/shared/workflow/config/string-node.d.ts +3 -10
- package/lib/shared/workflow/config/string-node.d.ts.map +1 -1
- package/lib/shared/workflow/config/timestamp-node.d.ts +3 -10
- package/lib/shared/workflow/config/timestamp-node.d.ts.map +1 -1
- package/lib/shared/workflow/deactivator.d.ts +10 -4
- package/lib/shared/workflow/deactivator.d.ts.map +1 -1
- package/lib/shared/workflow/executor.d.ts +12 -4
- package/lib/shared/workflow/executor.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/attio-attribute.d.ts +4 -10
- package/lib/shared/workflow/outcome-data/attio-attribute.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/attio-entity-reference.d.ts +3 -7
- package/lib/shared/workflow/outcome-data/attio-entity-reference.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/attio-list.d.ts +4 -10
- package/lib/shared/workflow/outcome-data/attio-list.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/attio-object.d.ts +4 -10
- package/lib/shared/workflow/outcome-data/attio-object.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/attio-select.d.ts +4 -10
- package/lib/shared/workflow/outcome-data/attio-select.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/boolean-node.d.ts +3 -10
- package/lib/shared/workflow/outcome-data/boolean-node.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/node.d.ts +2 -10
- package/lib/shared/workflow/outcome-data/node.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/number-node.d.ts +3 -10
- package/lib/shared/workflow/outcome-data/number-node.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/rich-text-node.d.ts +3 -10
- package/lib/shared/workflow/outcome-data/rich-text-node.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/runtime-value-of.d.ts +1 -3
- package/lib/shared/workflow/outcome-data/runtime-value-of.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/string-node.d.ts +3 -10
- package/lib/shared/workflow/outcome-data/string-node.d.ts.map +1 -1
- package/lib/shared/workflow/outcome-data/timestamp-node.d.ts +3 -10
- package/lib/shared/workflow/outcome-data/timestamp-node.d.ts.map +1 -1
- package/lib/spinners/determine-workspace.spinner.js +1 -2
- package/lib/spinners/get-app-info.spinner.js +1 -2
- package/lib/spinners/get-versions.spinner.js +1 -2
- package/lib/tsconfig.lib.tsbuildinfo +1 -1
- package/lib/util/assert-app-settings.js +1 -1
- package/lib/util/exit-with-missing-app-settings.js +1 -1
- package/lib/util/exit-with-missing-entry-point.js +1 -1
- package/lib/util/generate-settings-files.js +1 -1
- package/lib/util/realtime.js +1 -1
- package/package.json +3 -3
- package/lib/api/api.js +0 -183
- package/lib/api/fetcher.js +0 -69
- package/lib/api/schemas.js +0 -89
- package/lib/attio-logo.js +0 -24
- package/lib/auth/auth.js +0 -174
- package/lib/auth/keychain.js +0 -108
- package/lib/build/workflow-block-modules.js +0 -1
- package/lib/commands/init/create-project.js +0 -36
- package/lib/constants/settings-files.js +0 -2
- package/lib/env.js +0 -5
- package/lib/errors.js +0 -1
- package/lib/print-errors.js +0 -177
- package/lib/sdk-version.js +0 -1
- package/lib/shared/schema-utils/attio-attribute.d.ts +0 -12
- package/lib/shared/schema-utils/attio-attribute.d.ts.map +0 -1
- package/lib/shared/schema-utils/attio-attribute.js.map +0 -1
- package/lib/shared/schema-utils/attio-list.d.ts +0 -12
- package/lib/shared/schema-utils/attio-list.d.ts.map +0 -1
- package/lib/shared/schema-utils/attio-list.js.map +0 -1
- package/lib/shared/schema-utils/attio-object.d.ts +0 -12
- package/lib/shared/schema-utils/attio-object.d.ts.map +0 -1
- package/lib/shared/schema-utils/attio-object.js.map +0 -1
- package/lib/template/README.md +0 -21
- package/lib/template/biome.jsonc +0 -59
- package/lib/template/eslint.attio.config.js +0 -41
- package/lib/template/graphql.config.json +0 -9
- package/lib/template/package.json +0 -35
- package/lib/template/src/app.settings.ts +0 -8
- package/lib/template/src/app.ts +0 -10
- package/lib/template/src/assets/icon.png +0 -0
- package/lib/template/src/get-stoic-quote.server.ts +0 -14
- package/lib/template/src/hello-world-action.tsx +0 -18
- package/lib/template/src/hello-world-dialog.tsx +0 -27
- package/lib/template/src/stoic-quote.tsx +0 -21
- package/lib/template/tsconfig.json +0 -42
- package/lib/util/can-write.js +0 -11
- package/lib/util/copy-with-replace.js +0 -56
- package/lib/util/create-directory.js +0 -27
- package/lib/util/find-available-port.js +0 -37
- package/lib/util/hard-exit.js +0 -6
- package/lib/util/print-logo.js +0 -5
- package/lib/util/spinner.js +0 -61
- package/lib/util/text-gradient.js +0 -28
package/lib/api/fetcher.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { complete, errored, isErrored } from "@attio/fetchable-npm";
|
|
2
|
-
import { authenticator } from "../auth/auth.js";
|
|
3
|
-
import { API } from "../env.js";
|
|
4
|
-
export class Fetcher {
|
|
5
|
-
async _fetch({ path, fetchOptions, schema, authenticated, }) {
|
|
6
|
-
const tokenResult = authenticated === "Authenticated" ? await authenticator.ensureAuthed() : complete(null);
|
|
7
|
-
if (isErrored(tokenResult)) {
|
|
8
|
-
return errored({
|
|
9
|
-
code: "UNAUTHORIZED",
|
|
10
|
-
error: tokenResult.error,
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
const token = tokenResult.value;
|
|
14
|
-
try {
|
|
15
|
-
const response = await fetch(path.startsWith("https") ? path : `${API}${path}`, {
|
|
16
|
-
...fetchOptions,
|
|
17
|
-
headers: {
|
|
18
|
-
"x-attio-platform": "developer-cli",
|
|
19
|
-
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
20
|
-
"Content-Type": "application/json",
|
|
21
|
-
...fetchOptions.headers,
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
if (!response.ok) {
|
|
25
|
-
const errorText = await response.text();
|
|
26
|
-
return errored({
|
|
27
|
-
code: "HTTP_ERROR",
|
|
28
|
-
status: response.status,
|
|
29
|
-
message: errorText,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
const data = await response.json();
|
|
33
|
-
const result = schema.safeParse(data);
|
|
34
|
-
if (!result.success) {
|
|
35
|
-
return errored({
|
|
36
|
-
code: "INVALID_RESPONSE",
|
|
37
|
-
message: result.error.message,
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
return complete(result.data);
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
return errored({
|
|
44
|
-
code: "NETWORK_ERROR",
|
|
45
|
-
message: error instanceof Error ? error.message : String(error),
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
async get({ path, schema, headers = {}, authenticated = "Authenticated", }) {
|
|
50
|
-
return this._fetch({ path, fetchOptions: { method: "GET", headers }, schema, authenticated });
|
|
51
|
-
}
|
|
52
|
-
async post({ path, body, schema, headers = {}, authenticated = "Authenticated", }) {
|
|
53
|
-
return this._fetch({
|
|
54
|
-
path,
|
|
55
|
-
fetchOptions: {
|
|
56
|
-
method: "POST",
|
|
57
|
-
body: body instanceof URLSearchParams ? body.toString() : JSON.stringify(body),
|
|
58
|
-
headers: {
|
|
59
|
-
"Content-Type": body instanceof URLSearchParams
|
|
60
|
-
? "application/x-www-form-urlencoded"
|
|
61
|
-
: "application/json",
|
|
62
|
-
...headers,
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
schema,
|
|
66
|
-
authenticated,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}
|
package/lib/api/schemas.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
export const whoamiSchema = z.object({
|
|
3
|
-
user: z.object({
|
|
4
|
-
id: z.string(),
|
|
5
|
-
email_address: z.string(),
|
|
6
|
-
name: z.object({
|
|
7
|
-
full: z.string(),
|
|
8
|
-
}),
|
|
9
|
-
}),
|
|
10
|
-
});
|
|
11
|
-
export const workspaceResponseSchema = z.object({
|
|
12
|
-
workspace_id: z.string().uuid(),
|
|
13
|
-
slug: z.string(),
|
|
14
|
-
name: z.string(),
|
|
15
|
-
logo_url: z.string().nullable(),
|
|
16
|
-
});
|
|
17
|
-
export const TEST_WORKSPACES = [
|
|
18
|
-
workspaceResponseSchema.parse({
|
|
19
|
-
workspace_id: "a85e3bcf-a9a2-4df9-9e92-e708bf98d238",
|
|
20
|
-
slug: "test-slug",
|
|
21
|
-
name: "Test Workspace",
|
|
22
|
-
logo_url: null,
|
|
23
|
-
}),
|
|
24
|
-
];
|
|
25
|
-
export const listDevWorkspacesResponseSchema = z.object({
|
|
26
|
-
workspaces: z.array(workspaceResponseSchema),
|
|
27
|
-
});
|
|
28
|
-
export const createVersionSchema = z.object({
|
|
29
|
-
app_id: z.string().uuid(),
|
|
30
|
-
major: z.number(),
|
|
31
|
-
minor: z.number(),
|
|
32
|
-
app_prod_version_bundle_id: z.string(),
|
|
33
|
-
client_bundle_upload_url: z.string(),
|
|
34
|
-
server_bundle_upload_url: z.string(),
|
|
35
|
-
});
|
|
36
|
-
export const appInfoSchema = z.object({
|
|
37
|
-
app: z.object({
|
|
38
|
-
app_id: z.string().uuid(),
|
|
39
|
-
title: z.string(),
|
|
40
|
-
}),
|
|
41
|
-
});
|
|
42
|
-
export const TEST_APP_INFO = appInfoSchema.parse({
|
|
43
|
-
app: {
|
|
44
|
-
app_id: "0cbafb09-2ef7-473c-a048-08a5d190a395",
|
|
45
|
-
title: "Test App",
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
export const completeBundleUploadSchema = z.object({
|
|
49
|
-
success: z.literal(true),
|
|
50
|
-
});
|
|
51
|
-
export const startUploadSchema = z.object({
|
|
52
|
-
app_dev_version_id: z.string().uuid(),
|
|
53
|
-
app_dev_version_bundle_id: z.string().uuid(),
|
|
54
|
-
client_bundle_upload_url: z.string().url(),
|
|
55
|
-
server_bundle_upload_url: z.string().url(),
|
|
56
|
-
});
|
|
57
|
-
export const createDevVersionSchema = z.object({
|
|
58
|
-
app_id: z.string().uuid(),
|
|
59
|
-
app_dev_version_id: z.string(),
|
|
60
|
-
});
|
|
61
|
-
export const installationSchema = z.object({
|
|
62
|
-
app_id: z.string().uuid(),
|
|
63
|
-
workspace_id: z.string(),
|
|
64
|
-
});
|
|
65
|
-
export const versionSchema = z.object({
|
|
66
|
-
major: z.number(),
|
|
67
|
-
minor: z.number(),
|
|
68
|
-
created_at: z.string(),
|
|
69
|
-
released_at: z.string().nullable().optional(),
|
|
70
|
-
is_published: z.boolean(),
|
|
71
|
-
num_installations: z.number(),
|
|
72
|
-
publication_status: z.enum(["private", "in-review", "published", "rejected"]),
|
|
73
|
-
});
|
|
74
|
-
export const versionsSchema = z.object({
|
|
75
|
-
app_prod_versions: z.array(versionSchema),
|
|
76
|
-
});
|
|
77
|
-
export const tokenResponseSchema = z.object({
|
|
78
|
-
access_token: z.string(),
|
|
79
|
-
token_type: z.literal("Bearer"),
|
|
80
|
-
refresh_token: z.string(),
|
|
81
|
-
expires_in: z.number(),
|
|
82
|
-
});
|
|
83
|
-
export const ablyAuthResponseSchema = z.object({
|
|
84
|
-
capability: z.string(),
|
|
85
|
-
clientId: z.string().optional(),
|
|
86
|
-
expires: z.number(),
|
|
87
|
-
issued: z.number(),
|
|
88
|
-
token: z.string(),
|
|
89
|
-
});
|
package/lib/attio-logo.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export const attioLogo = `
|
|
2
|
-
▟██▛╲
|
|
3
|
-
▟██▛ ╲
|
|
4
|
-
▟██▛ ╱
|
|
5
|
-
▟██▛ ╱
|
|
6
|
-
▟██▛ ╱ ▟█▛╲
|
|
7
|
-
🬇██▛ ╱ ▟█▛ ╲
|
|
8
|
-
▜█▙ ╱ 🬇██▙ ╱
|
|
9
|
-
▜█▙╱ ▜█▙╱
|
|
10
|
-
`;
|
|
11
|
-
export const attioName = `
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
▟ ▟ ██
|
|
15
|
-
██ ██
|
|
16
|
-
▟███▙▟ ████████████ ██ ▟███▙
|
|
17
|
-
▟█ ██ ██ ██ ██ █ █
|
|
18
|
-
██ ██ ██ ██ ██ █ █
|
|
19
|
-
▜████▛█ ▜███ ▜███ ██ ▜███▛
|
|
20
|
-
`;
|
|
21
|
-
export const attioLogoAndName = `${attioLogo
|
|
22
|
-
.split("\n")
|
|
23
|
-
.map((line, i) => i < attioLogo.split("\n").length - 1 ? `${line} ${attioName.split("\n")[i] || ""}` : line)
|
|
24
|
-
.join("\n")}`;
|
package/lib/auth/auth.js
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { createHash, randomBytes } from "crypto";
|
|
2
|
-
import { serve } from "@hono/node-server";
|
|
3
|
-
import { Hono } from "hono";
|
|
4
|
-
import open from "open";
|
|
5
|
-
import { complete, errored, isErrored } from "@attio/fetchable-npm";
|
|
6
|
-
import { api } from "../api/api.js";
|
|
7
|
-
import { APP } from "../env.js";
|
|
8
|
-
import { printFetcherError, printKeychainError } from "../print-errors.js";
|
|
9
|
-
import { findAvailablePort } from "../util/find-available-port.js";
|
|
10
|
-
import { getKeychain } from "./keychain.js";
|
|
11
|
-
const MAX_TIMEOUT = 2_147_483_647;
|
|
12
|
-
class AuthenticatorImpl {
|
|
13
|
-
clientId = "f881c6f1-82d7-48a5-a581-649596167845";
|
|
14
|
-
isRefreshingToken = false;
|
|
15
|
-
refreshTimeout = null;
|
|
16
|
-
async ensureAuthed() {
|
|
17
|
-
const existingTokenResult = await getKeychain().load();
|
|
18
|
-
if (isErrored(existingTokenResult)) {
|
|
19
|
-
return this.promptToAuthenticate();
|
|
20
|
-
}
|
|
21
|
-
const existingToken = existingTokenResult.value;
|
|
22
|
-
if (existingToken === null || existingToken.expires_at < Date.now()) {
|
|
23
|
-
return this.promptToAuthenticate();
|
|
24
|
-
}
|
|
25
|
-
this.scheduleRefresh(existingToken);
|
|
26
|
-
return complete(existingToken.access_token);
|
|
27
|
-
}
|
|
28
|
-
async promptToAuthenticate() {
|
|
29
|
-
if (process.env.NODE_ENV !== "test") {
|
|
30
|
-
process.stdout.write("You need to log in with Attio. Press Enter to continue...\n\n");
|
|
31
|
-
await new Promise((resolve) => process.stdin.once("data", resolve));
|
|
32
|
-
}
|
|
33
|
-
return this.authenticate();
|
|
34
|
-
}
|
|
35
|
-
async authenticate() {
|
|
36
|
-
const existingTokenResult = await getKeychain().load();
|
|
37
|
-
if (isErrored(existingTokenResult)) {
|
|
38
|
-
return existingTokenResult;
|
|
39
|
-
}
|
|
40
|
-
const existingToken = existingTokenResult.value;
|
|
41
|
-
if (existingToken !== null && existingToken.expires_at > Date.now()) {
|
|
42
|
-
return complete(existingToken.access_token);
|
|
43
|
-
}
|
|
44
|
-
const verifier = randomBytes(32);
|
|
45
|
-
const verifierString = verifier.toString("base64url");
|
|
46
|
-
const hash = createHash("sha256");
|
|
47
|
-
hash.update(verifier);
|
|
48
|
-
const challenge = hash.digest();
|
|
49
|
-
const challengeString = challenge.toString("base64url");
|
|
50
|
-
const state = randomBytes(32).toString("base64url");
|
|
51
|
-
const port = await findAvailablePort(3000, 65000);
|
|
52
|
-
const redirectUri = `http://localhost:${port}`;
|
|
53
|
-
let resolveAsyncResult;
|
|
54
|
-
const asyncResult = new Promise((resolve) => {
|
|
55
|
-
resolveAsyncResult = resolve;
|
|
56
|
-
});
|
|
57
|
-
const app = new Hono();
|
|
58
|
-
let serverRef;
|
|
59
|
-
app.get("/", async (c) => {
|
|
60
|
-
const query = c.req.query();
|
|
61
|
-
const receivedCode = query.authorization_code;
|
|
62
|
-
const receivedState = query.state;
|
|
63
|
-
if (receivedState !== state) {
|
|
64
|
-
resolveAsyncResult(errored({ code: "OAUTH_STATE_MISMATCH" }));
|
|
65
|
-
}
|
|
66
|
-
if (!receivedCode) {
|
|
67
|
-
resolveAsyncResult(errored({ code: "NO_AUTHORIZATION_CODE" }));
|
|
68
|
-
}
|
|
69
|
-
const tokenResult = await api.exchangeToken({
|
|
70
|
-
receivedCode,
|
|
71
|
-
verifierString,
|
|
72
|
-
redirectUri,
|
|
73
|
-
clientId: this.clientId,
|
|
74
|
-
});
|
|
75
|
-
setTimeout(() => {
|
|
76
|
-
serverRef.close();
|
|
77
|
-
resolveAsyncResult(tokenResult);
|
|
78
|
-
}, 1_000);
|
|
79
|
-
return c.html(`
|
|
80
|
-
<html>
|
|
81
|
-
<body>
|
|
82
|
-
<script>window.location.href = '${APP}/authorized';</script>
|
|
83
|
-
</body>
|
|
84
|
-
</html>
|
|
85
|
-
`);
|
|
86
|
-
});
|
|
87
|
-
serverRef = serve({
|
|
88
|
-
fetch: app.fetch,
|
|
89
|
-
port,
|
|
90
|
-
});
|
|
91
|
-
try {
|
|
92
|
-
const authUrl = new URL(`${APP}/oidc/authorize`);
|
|
93
|
-
authUrl.searchParams.append("scope", "openid offline_access");
|
|
94
|
-
authUrl.searchParams.append("response_type", "code");
|
|
95
|
-
authUrl.searchParams.append("client_id", this.clientId);
|
|
96
|
-
authUrl.searchParams.append("redirect_uri", redirectUri);
|
|
97
|
-
authUrl.searchParams.append("state", state);
|
|
98
|
-
authUrl.searchParams.append("code_challenge", challengeString);
|
|
99
|
-
authUrl.searchParams.append("code_challenge_method", "S256");
|
|
100
|
-
await open(authUrl.toString());
|
|
101
|
-
const tokenResult = await asyncResult;
|
|
102
|
-
if (isErrored(tokenResult)) {
|
|
103
|
-
return tokenResult;
|
|
104
|
-
}
|
|
105
|
-
const token = tokenResult.value;
|
|
106
|
-
process.stdout.write("🔑 Saving new token to keychain\n");
|
|
107
|
-
const keychainToken = {
|
|
108
|
-
access_token: token.access_token,
|
|
109
|
-
refresh_token: token.refresh_token,
|
|
110
|
-
token_type: token.token_type,
|
|
111
|
-
expires_at: Date.now() + token.expires_in * 1000,
|
|
112
|
-
};
|
|
113
|
-
const saveResult = await getKeychain().save(keychainToken);
|
|
114
|
-
if (isErrored(saveResult)) {
|
|
115
|
-
return saveResult;
|
|
116
|
-
}
|
|
117
|
-
this.scheduleRefresh(keychainToken);
|
|
118
|
-
return complete(token.access_token);
|
|
119
|
-
}
|
|
120
|
-
finally {
|
|
121
|
-
serverRef.close();
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
scheduleRefresh(token) {
|
|
125
|
-
if (this.refreshTimeout !== null) {
|
|
126
|
-
clearTimeout(this.refreshTimeout);
|
|
127
|
-
}
|
|
128
|
-
const delay = Math.min(Math.max(0, (token.expires_at - Date.now()) / 10), MAX_TIMEOUT);
|
|
129
|
-
this.refreshTimeout = setTimeout(async () => {
|
|
130
|
-
if (this.isRefreshingToken) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
this.isRefreshingToken = true;
|
|
134
|
-
try {
|
|
135
|
-
await this.refreshToken(token);
|
|
136
|
-
}
|
|
137
|
-
finally {
|
|
138
|
-
this.isRefreshingToken = false;
|
|
139
|
-
this.refreshTimeout = null;
|
|
140
|
-
}
|
|
141
|
-
}, delay);
|
|
142
|
-
}
|
|
143
|
-
async refreshToken(token) {
|
|
144
|
-
const refreshTokenResult = await api.refreshToken({
|
|
145
|
-
refreshToken: token.refresh_token,
|
|
146
|
-
clientId: this.clientId,
|
|
147
|
-
});
|
|
148
|
-
if (isErrored(refreshTokenResult)) {
|
|
149
|
-
printFetcherError("Error refreshing token", refreshTokenResult.error);
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const refreshedToken = refreshTokenResult.value;
|
|
153
|
-
const keychainToken = {
|
|
154
|
-
access_token: refreshedToken.access_token,
|
|
155
|
-
refresh_token: refreshedToken.refresh_token,
|
|
156
|
-
token_type: refreshedToken.token_type,
|
|
157
|
-
expires_at: Date.now() + refreshedToken.expires_in * 1000,
|
|
158
|
-
};
|
|
159
|
-
const saveResult = await getKeychain().save(keychainToken);
|
|
160
|
-
if (isErrored(saveResult)) {
|
|
161
|
-
printKeychainError(saveResult.error);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
this.scheduleRefresh(keychainToken);
|
|
165
|
-
}
|
|
166
|
-
async logout() {
|
|
167
|
-
if (this.refreshTimeout !== null) {
|
|
168
|
-
clearTimeout(this.refreshTimeout);
|
|
169
|
-
this.refreshTimeout = null;
|
|
170
|
-
}
|
|
171
|
-
await getKeychain().delete();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
export const authenticator = new AuthenticatorImpl();
|
package/lib/auth/keychain.js
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { complete, errored, isErrored } from "@attio/fetchable-npm";
|
|
3
|
-
const authTokenSchema = z.object({
|
|
4
|
-
access_token: z.string(),
|
|
5
|
-
refresh_token: z.string(),
|
|
6
|
-
token_type: z.literal("Bearer"),
|
|
7
|
-
expires_at: z.number(),
|
|
8
|
-
});
|
|
9
|
-
class KeytarKeychain {
|
|
10
|
-
SERVICE_NAME = "attio-cli";
|
|
11
|
-
ACCOUNT_NAME = "developer";
|
|
12
|
-
keytarPromise;
|
|
13
|
-
constructor() {
|
|
14
|
-
this.keytarPromise = import("keytar").then((module) => module.default);
|
|
15
|
-
}
|
|
16
|
-
async save(token) {
|
|
17
|
-
const keytar = await this.keytarPromise;
|
|
18
|
-
try {
|
|
19
|
-
await keytar.setPassword(this.SERVICE_NAME, this.ACCOUNT_NAME, JSON.stringify(token));
|
|
20
|
-
return complete(undefined);
|
|
21
|
-
}
|
|
22
|
-
catch (error) {
|
|
23
|
-
return errored({
|
|
24
|
-
code: "SAVE_KEYCHAIN_ERROR",
|
|
25
|
-
error,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
async load() {
|
|
30
|
-
const keytar = await this.keytarPromise;
|
|
31
|
-
let unparsedToken = null;
|
|
32
|
-
try {
|
|
33
|
-
unparsedToken = await keytar.getPassword(this.SERVICE_NAME, this.ACCOUNT_NAME);
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
return errored({ code: "LOAD_KEYCHAIN_ERROR", error });
|
|
37
|
-
}
|
|
38
|
-
if (unparsedToken === null) {
|
|
39
|
-
return complete(null);
|
|
40
|
-
}
|
|
41
|
-
let jsonToken;
|
|
42
|
-
try {
|
|
43
|
-
jsonToken = JSON.parse(unparsedToken);
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
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);
|
|
52
|
-
}
|
|
53
|
-
const parsedToken = authTokenSchema.safeParse(jsonToken);
|
|
54
|
-
if (!parsedToken.success) {
|
|
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);
|
|
68
|
-
}
|
|
69
|
-
return complete(parsedToken.data);
|
|
70
|
-
}
|
|
71
|
-
async delete() {
|
|
72
|
-
const keytar = await this.keytarPromise;
|
|
73
|
-
try {
|
|
74
|
-
await keytar.deletePassword(this.SERVICE_NAME, this.ACCOUNT_NAME);
|
|
75
|
-
return complete(undefined);
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
return errored({
|
|
79
|
-
code: "DELETE_KEYCHAIN_ERROR",
|
|
80
|
-
error,
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
class TestKeychain {
|
|
86
|
-
TEST_TOKEN = {
|
|
87
|
-
access_token: "TEST",
|
|
88
|
-
refresh_token: "TEST",
|
|
89
|
-
token_type: "Bearer",
|
|
90
|
-
expires_at: Number.MAX_SAFE_INTEGER,
|
|
91
|
-
};
|
|
92
|
-
async save(_token) {
|
|
93
|
-
return complete(undefined);
|
|
94
|
-
}
|
|
95
|
-
async load() {
|
|
96
|
-
return complete(this.TEST_TOKEN);
|
|
97
|
-
}
|
|
98
|
-
async delete() {
|
|
99
|
-
return complete(undefined);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
let keychain = null;
|
|
103
|
-
export const getKeychain = () => {
|
|
104
|
-
if (keychain === null) {
|
|
105
|
-
keychain = process.env.NODE_ENV === "test" ? new TestKeychain() : new KeytarKeychain();
|
|
106
|
-
}
|
|
107
|
-
return keychain;
|
|
108
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { fileURLToPath } from "url";
|
|
4
|
-
import { complete, errored, isErrored } from "@attio/fetchable-npm";
|
|
5
|
-
import { canWrite } from "../../util/can-write.js";
|
|
6
|
-
import { copyWithTransform } from "../../util/copy-with-replace.js";
|
|
7
|
-
import { createDirectory } from "../../util/create-directory.js";
|
|
8
|
-
import { spinnerify } from "../../util/spinner.js";
|
|
9
|
-
export async function createProject({ appSlug, appInfo, }) {
|
|
10
|
-
return await spinnerify("Creating project...", "Project created", async () => {
|
|
11
|
-
const cwd = process.cwd();
|
|
12
|
-
const projectPath = path.join(cwd, appSlug);
|
|
13
|
-
if (existsSync(projectPath)) {
|
|
14
|
-
return errored({ code: "DIRECTORY_ALREADY_EXISTS", path: projectPath });
|
|
15
|
-
}
|
|
16
|
-
if (!canWrite(cwd)) {
|
|
17
|
-
return errored({ code: "WRITE_ACCESS_DENIED", path: cwd });
|
|
18
|
-
}
|
|
19
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
-
const projectDirResult = await createDirectory(appSlug);
|
|
21
|
-
if (isErrored(projectDirResult)) {
|
|
22
|
-
return projectDirResult;
|
|
23
|
-
}
|
|
24
|
-
const projectDir = projectDirResult.value;
|
|
25
|
-
const templateDir = path.resolve(__dirname, "../../template");
|
|
26
|
-
const transform = (contents) => contents
|
|
27
|
-
.replaceAll("title-to-be-replaced", appInfo.title)
|
|
28
|
-
.replaceAll("id-to-be-replaced", appInfo.app_id)
|
|
29
|
-
.replaceAll("slug-to-be-replaced", appSlug);
|
|
30
|
-
const result = await copyWithTransform(templateDir, projectDir, transform);
|
|
31
|
-
if (isErrored(result)) {
|
|
32
|
-
return result;
|
|
33
|
-
}
|
|
34
|
-
return complete(undefined);
|
|
35
|
-
});
|
|
36
|
-
}
|
package/lib/env.js
DELETED
package/lib/errors.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|