attio 0.0.1-experimental.20250324.1 → 0.0.1-experimental.20250325.1
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/fetch-versions.js +0 -24
- package/lib/api/get-app-info.js +2 -2
- package/lib/commands/dev.js +7 -1
- package/lib/commands/version/list.js +4 -5
- package/lib/machines/actions.js +1 -0
- package/lib/machines/actors.js +94 -34
- package/lib/machines/create-version-machine.js +116 -123
- package/lib/machines/dev-machine.js +146 -358
- package/lib/machines/init-machine.js +57 -134
- package/lib/util/find-available-port.js +14 -4
- package/package.json +1 -1
- package/lib/api/add-connection-definition.js +0 -32
- package/lib/api/load-app-id.js +0 -9
- package/lib/api/load-dev-slug.js +0 -9
- package/lib/schema.js +0 -33
- package/lib/templates/common/attio.json +0 -4
- package/lib/util/app-config.js +0 -51
- package/lib/util/load-developer-config.js +0 -92
- package/lib/util/print-install-instructions.js +0 -32
|
@@ -2,18 +2,14 @@ import { existsSync } from "fs";
|
|
|
2
2
|
import Spinner from "tiny-spinner";
|
|
3
3
|
import path, { join } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
|
-
import { assign, setup,
|
|
5
|
+
import { assign, setup, fromPromise } from "xstate";
|
|
6
6
|
import { copyWithTransform } from "../util/copy-with-replace.js";
|
|
7
7
|
import { createDirectory } from "../util/create-directory.js";
|
|
8
|
-
import { loadDeveloperConfig } from "../util/load-developer-config.js";
|
|
9
8
|
import { canWrite } from "../util/validate-slug.js";
|
|
10
|
-
import { ask, askWithChoices } from "./actors.js";
|
|
11
|
-
import {
|
|
9
|
+
import { ask, askWithChoices, authenticate, loadAppInfo } from "./actors.js";
|
|
10
|
+
import { printLogo, showActorError } from "./actions.js";
|
|
12
11
|
import chalk from "chalk";
|
|
13
12
|
import boxen from "boxen";
|
|
14
|
-
import { printInstallInstructions } from "../util/print-install-instructions.js";
|
|
15
|
-
import { getAppInfo } from "../api/get-app-info.js";
|
|
16
|
-
import { ensureAuthed } from "../api/ensure-authed.js";
|
|
17
13
|
export const languages = [
|
|
18
14
|
{ name: "TypeScript (recommended)", value: "typescript" },
|
|
19
15
|
{ name: "JavaScript", value: "javascript" },
|
|
@@ -21,96 +17,50 @@ export const languages = [
|
|
|
21
17
|
export const initMachine = setup({
|
|
22
18
|
types: {
|
|
23
19
|
context: {},
|
|
24
|
-
events: {},
|
|
25
20
|
input: {},
|
|
26
21
|
},
|
|
27
22
|
actors: {
|
|
28
23
|
ask,
|
|
29
24
|
askWithChoices,
|
|
30
|
-
createProject:
|
|
25
|
+
createProject: fromPromise(async ({ input }) => {
|
|
31
26
|
const { appSlug, appInfo, language } = input;
|
|
32
|
-
const create = async () => {
|
|
33
|
-
const spinner = new Spinner();
|
|
34
|
-
try {
|
|
35
|
-
if (existsSync(join(process.cwd(), appSlug))) {
|
|
36
|
-
sendBack({
|
|
37
|
-
type: "Error",
|
|
38
|
-
error: `Directory "${appSlug}" already exists`,
|
|
39
|
-
});
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
if (!canWrite(process.cwd())) {
|
|
43
|
-
sendBack({
|
|
44
|
-
type: "Error",
|
|
45
|
-
error: "Write access denied to current directory",
|
|
46
|
-
});
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
50
|
-
const projectDir = createDirectory(appSlug);
|
|
51
|
-
const templatesDir = path.resolve(__dirname, "../templates", language);
|
|
52
|
-
const commonDir = path.resolve(__dirname, "../templates", "common");
|
|
53
|
-
const transform = (contents) => contents
|
|
54
|
-
.replaceAll("title-to-be-replaced", appInfo.title)
|
|
55
|
-
.replaceAll("id-to-be-replaced", appInfo.app_id)
|
|
56
|
-
.replaceAll("slug-to-be-replaced", appSlug);
|
|
57
|
-
spinner.start("Creating project...");
|
|
58
|
-
await Promise.all([
|
|
59
|
-
copyWithTransform(templatesDir, projectDir, transform),
|
|
60
|
-
copyWithTransform(commonDir, projectDir, transform),
|
|
61
|
-
]);
|
|
62
|
-
spinner.success("Project created");
|
|
63
|
-
sendBack({ type: "Success" });
|
|
64
|
-
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
spinner.error("Error creating project");
|
|
67
|
-
sendBack({ type: "Error", error: error.message });
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
create();
|
|
71
|
-
}),
|
|
72
|
-
loadConfig: fromCallback(({ sendBack }) => {
|
|
73
|
-
const load = async () => {
|
|
74
|
-
const config = await loadDeveloperConfig();
|
|
75
|
-
if (typeof config === "string") {
|
|
76
|
-
sendBack({ type: "No Config", configError: config });
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const token = await ensureAuthed();
|
|
80
|
-
sendBack({
|
|
81
|
-
type: "Config Loaded",
|
|
82
|
-
token,
|
|
83
|
-
developerSlug: config.developer_slug,
|
|
84
|
-
});
|
|
85
|
-
};
|
|
86
|
-
load();
|
|
87
|
-
}),
|
|
88
|
-
loadAppInfo: fromPromise(async ({ input: { token, developerSlug, appSlug } }) => {
|
|
89
27
|
const spinner = new Spinner();
|
|
90
|
-
spinner.start("Loading app information...");
|
|
91
28
|
try {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
29
|
+
if (existsSync(join(process.cwd(), appSlug))) {
|
|
30
|
+
throw new Error(`Directory "${appSlug}" already exists`);
|
|
31
|
+
}
|
|
32
|
+
if (!canWrite(process.cwd())) {
|
|
33
|
+
throw new Error("Write access denied to current directory");
|
|
96
34
|
}
|
|
97
|
-
|
|
98
|
-
|
|
35
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
36
|
+
const projectDir = createDirectory(appSlug);
|
|
37
|
+
const templatesDir = path.resolve(__dirname, "../templates", language);
|
|
38
|
+
const commonDir = path.resolve(__dirname, "../templates", "common");
|
|
39
|
+
const transform = (contents) => contents
|
|
40
|
+
.replaceAll("title-to-be-replaced", appInfo.title)
|
|
41
|
+
.replaceAll("id-to-be-replaced", appInfo.app_id)
|
|
42
|
+
.replaceAll("slug-to-be-replaced", appSlug);
|
|
43
|
+
spinner.start("Creating project...");
|
|
44
|
+
await Promise.all([
|
|
45
|
+
copyWithTransform(templatesDir, projectDir, transform),
|
|
46
|
+
copyWithTransform(commonDir, projectDir, transform),
|
|
47
|
+
]);
|
|
48
|
+
spinner.success("Project created");
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
spinner.error("Error creating project");
|
|
52
|
+
throw error;
|
|
99
53
|
}
|
|
100
54
|
finally {
|
|
101
55
|
spinner.stop();
|
|
102
56
|
}
|
|
103
57
|
}),
|
|
58
|
+
authenticate,
|
|
59
|
+
loadAppInfo,
|
|
104
60
|
},
|
|
105
61
|
actions: {
|
|
106
62
|
printLogo,
|
|
107
|
-
|
|
108
|
-
error: () => undefined,
|
|
109
|
-
}),
|
|
110
|
-
showError,
|
|
111
|
-
showConfigInstructions: ({ context: { configError } }) => {
|
|
112
|
-
printInstallInstructions(configError);
|
|
113
|
-
},
|
|
63
|
+
showActorError,
|
|
114
64
|
showInstructions: (_, params) => {
|
|
115
65
|
process.stdout.write("\n" + chalk.green(`SUCCESS!! 🎉 Your app directory has been created.`) + "\n");
|
|
116
66
|
process.stdout.write("\nTo get started, run:\n");
|
|
@@ -125,14 +75,10 @@ export const initMachine = setup({
|
|
|
125
75
|
language: (_, params) => params.output,
|
|
126
76
|
}),
|
|
127
77
|
setAppInfo: assign({
|
|
128
|
-
appInfo: (_, params) => params.
|
|
129
|
-
}),
|
|
130
|
-
setConfig: assign({
|
|
131
|
-
token: (_, params) => params.token,
|
|
132
|
-
developerSlug: (_, params) => params.developerSlug,
|
|
78
|
+
appInfo: (_, params) => params.output,
|
|
133
79
|
}),
|
|
134
|
-
|
|
135
|
-
|
|
80
|
+
setToken: assign({
|
|
81
|
+
token: (_, params) => params.output,
|
|
136
82
|
}),
|
|
137
83
|
},
|
|
138
84
|
guards: {
|
|
@@ -141,8 +87,6 @@ export const initMachine = setup({
|
|
|
141
87
|
},
|
|
142
88
|
}).createMachine({
|
|
143
89
|
context: ({ input }) => ({
|
|
144
|
-
availablePackageManagers: ["npm"],
|
|
145
|
-
developerSlug: "",
|
|
146
90
|
appId: "",
|
|
147
91
|
token: "",
|
|
148
92
|
...input,
|
|
@@ -167,16 +111,6 @@ export const initMachine = setup({
|
|
|
167
111
|
},
|
|
168
112
|
},
|
|
169
113
|
"Creating Project": {
|
|
170
|
-
on: {
|
|
171
|
-
Error: {
|
|
172
|
-
target: "Error",
|
|
173
|
-
actions: { type: "showError", params: ({ event }) => event },
|
|
174
|
-
},
|
|
175
|
-
Success: {
|
|
176
|
-
target: "Success",
|
|
177
|
-
reenter: true,
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
114
|
invoke: {
|
|
181
115
|
src: "createProject",
|
|
182
116
|
input: ({ context }) => ({
|
|
@@ -184,6 +118,11 @@ export const initMachine = setup({
|
|
|
184
118
|
appInfo: context.appInfo,
|
|
185
119
|
appSlug: context.appSlug,
|
|
186
120
|
}),
|
|
121
|
+
onDone: "Success",
|
|
122
|
+
onError: {
|
|
123
|
+
target: "Error",
|
|
124
|
+
actions: { type: "showActorError", params: ({ event }) => event },
|
|
125
|
+
},
|
|
187
126
|
},
|
|
188
127
|
},
|
|
189
128
|
"Error": {
|
|
@@ -215,55 +154,39 @@ export const initMachine = setup({
|
|
|
215
154
|
{
|
|
216
155
|
src: "loadAppInfo",
|
|
217
156
|
input: ({ context }) => context,
|
|
218
|
-
onDone:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
params: ({ event }) => ({ appInfo: event.output }),
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
target: "Error",
|
|
229
|
-
reenter: true,
|
|
157
|
+
onDone: {
|
|
158
|
+
target: "Do we need language?",
|
|
159
|
+
guard: { type: "app exists", params: ({ event }) => event },
|
|
160
|
+
actions: {
|
|
161
|
+
type: "setAppInfo",
|
|
162
|
+
params: ({ event }) => event,
|
|
230
163
|
},
|
|
231
|
-
|
|
164
|
+
},
|
|
232
165
|
onError: {
|
|
233
166
|
target: "Error",
|
|
234
167
|
actions: {
|
|
235
|
-
type: "
|
|
236
|
-
params: ({ event }) =>
|
|
237
|
-
error: event.error instanceof Error
|
|
238
|
-
? event.error.message
|
|
239
|
-
: String(event.error),
|
|
240
|
-
}),
|
|
168
|
+
type: "showActorError",
|
|
169
|
+
params: ({ event }) => event,
|
|
241
170
|
},
|
|
242
171
|
},
|
|
243
172
|
},
|
|
244
173
|
],
|
|
245
174
|
},
|
|
246
|
-
"
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
175
|
+
"Authenticate": {
|
|
176
|
+
invoke: {
|
|
177
|
+
src: "authenticate",
|
|
178
|
+
onError: {
|
|
179
|
+
target: "Error",
|
|
180
|
+
reenter: true,
|
|
181
|
+
actions: { type: "showActorError", params: ({ event }) => event },
|
|
251
182
|
},
|
|
252
|
-
|
|
183
|
+
onDone: {
|
|
253
184
|
target: "Loading App Info",
|
|
254
|
-
actions: { type: "
|
|
255
|
-
reenter: true,
|
|
185
|
+
actions: { type: "setToken", params: ({ event }) => event },
|
|
256
186
|
},
|
|
257
187
|
},
|
|
258
|
-
invoke: {
|
|
259
|
-
src: "loadConfig",
|
|
260
|
-
},
|
|
261
|
-
},
|
|
262
|
-
"Show config instructions": {
|
|
263
|
-
type: "final",
|
|
264
|
-
entry: "showConfigInstructions",
|
|
265
188
|
},
|
|
266
189
|
},
|
|
267
|
-
initial: "
|
|
190
|
+
initial: "Authenticate",
|
|
268
191
|
entry: "printLogo",
|
|
269
192
|
});
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import net from "net";
|
|
2
2
|
async function isPortAvailable(port) {
|
|
3
3
|
return new Promise((resolve) => {
|
|
4
|
-
const
|
|
5
|
-
|
|
4
|
+
const portTester = net.createConnection(port, "127.0.0.1");
|
|
5
|
+
portTester.setTimeout(1000);
|
|
6
|
+
const cleanup = () => {
|
|
7
|
+
portTester.removeAllListeners();
|
|
8
|
+
portTester.destroy();
|
|
9
|
+
};
|
|
10
|
+
portTester.once("error", (err) => {
|
|
11
|
+
cleanup();
|
|
6
12
|
if (err.code === "ECONNREFUSED") {
|
|
7
13
|
resolve(true);
|
|
8
14
|
}
|
|
@@ -10,8 +16,12 @@ async function isPortAvailable(port) {
|
|
|
10
16
|
resolve(false);
|
|
11
17
|
}
|
|
12
18
|
});
|
|
13
|
-
|
|
14
|
-
|
|
19
|
+
portTester.once("connect", () => {
|
|
20
|
+
cleanup();
|
|
21
|
+
resolve(false);
|
|
22
|
+
});
|
|
23
|
+
portTester.once("timeout", () => {
|
|
24
|
+
cleanup();
|
|
15
25
|
resolve(false);
|
|
16
26
|
});
|
|
17
27
|
});
|
package/package.json
CHANGED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { v4 as uuid } from "uuid";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { API } from "../env.js";
|
|
4
|
-
import { handleError } from "./handle-error.js";
|
|
5
|
-
import { makeHeaders } from "./make-headers.js";
|
|
6
|
-
const addConnectionSchema = z.object({
|
|
7
|
-
app_id: z.string(),
|
|
8
|
-
connection_definition_id: z.string(),
|
|
9
|
-
});
|
|
10
|
-
export async function addConnectionDefinition({ token, appId, label, description, global, connectionType, clientId, clientSecret, authorizeUrl, accessTokenUrl, major, scopes, }) {
|
|
11
|
-
const connectionDefinitionId = uuid();
|
|
12
|
-
const body = {
|
|
13
|
-
connection_type: connectionType,
|
|
14
|
-
label,
|
|
15
|
-
description,
|
|
16
|
-
global,
|
|
17
|
-
};
|
|
18
|
-
if (connectionType === "oauth2-code") {
|
|
19
|
-
body.authorize_url = authorizeUrl;
|
|
20
|
-
body.access_token_url = accessTokenUrl;
|
|
21
|
-
body.client_id = clientId;
|
|
22
|
-
body.client_secret = clientSecret;
|
|
23
|
-
body.scopes = scopes.split(",").map((scope) => scope.trim());
|
|
24
|
-
}
|
|
25
|
-
const response = await fetch(`${API}/apps/${appId}/versions/${major}/connection-definitions/${connectionDefinitionId}`, {
|
|
26
|
-
method: "PUT",
|
|
27
|
-
headers: makeHeaders(token),
|
|
28
|
-
body: JSON.stringify(body),
|
|
29
|
-
});
|
|
30
|
-
await handleError(response);
|
|
31
|
-
return addConnectionSchema.parse(await response.json());
|
|
32
|
-
}
|
package/lib/api/load-app-id.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { loadAppConfigFile } from "../util/app-config.js";
|
|
2
|
-
export function loadAppId() {
|
|
3
|
-
const appConfig = loadAppConfigFile();
|
|
4
|
-
if (typeof appConfig === "string") {
|
|
5
|
-
process.stderr.write(`❌ App config not found\n\n${appConfig}\n`);
|
|
6
|
-
process.exit(1);
|
|
7
|
-
}
|
|
8
|
-
return appConfig.id;
|
|
9
|
-
}
|
package/lib/api/load-dev-slug.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { loadDeveloperConfig } from "../util/load-developer-config.js";
|
|
2
|
-
export async function loadDevSlug() {
|
|
3
|
-
const devConfig = await loadDeveloperConfig();
|
|
4
|
-
if (typeof devConfig === "string") {
|
|
5
|
-
process.stderr.write(`❌ Developer config not found\n\n${devConfig}\n`);
|
|
6
|
-
process.exit(1);
|
|
7
|
-
}
|
|
8
|
-
return devConfig.developer_slug;
|
|
9
|
-
}
|
package/lib/schema.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
3
|
-
import { fromError } from "zod-validation-error";
|
|
4
|
-
const configSchema = z.object({
|
|
5
|
-
slug: z.string().describe("A unique slug for the app"),
|
|
6
|
-
id: z.string().uuid().describe("A unique ID for the app"),
|
|
7
|
-
major: z.number().int().min(1).default(1).describe("The major version of the app"),
|
|
8
|
-
minor: z.number().int().min(0).default(0).describe("The minor version of the app"),
|
|
9
|
-
connection: z
|
|
10
|
-
.object({
|
|
11
|
-
connection_type: z.enum(["secret", "oauth2-code"]).describe("The type of connection"),
|
|
12
|
-
audience: z
|
|
13
|
-
.enum(["workspace", "workspace-member"])
|
|
14
|
-
.describe("The connection's audience"),
|
|
15
|
-
})
|
|
16
|
-
.nullable()
|
|
17
|
-
.default(null)
|
|
18
|
-
.describe("Connections to other services"),
|
|
19
|
-
});
|
|
20
|
-
export const emptyConfig = {
|
|
21
|
-
slug: "",
|
|
22
|
-
id: "",
|
|
23
|
-
major: 1,
|
|
24
|
-
minor: 0,
|
|
25
|
-
connection: null,
|
|
26
|
-
};
|
|
27
|
-
export function parseConfig(json) {
|
|
28
|
-
const result = configSchema.safeParse(json);
|
|
29
|
-
if (result.success)
|
|
30
|
-
return result.data;
|
|
31
|
-
throw fromError(result.error).message;
|
|
32
|
-
}
|
|
33
|
-
export const getConfigJsonSchema = () => zodToJsonSchema(configSchema);
|
package/lib/util/app-config.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
-
import { parseConfig } from "../schema.js";
|
|
3
|
-
const ERRORS = {
|
|
4
|
-
noAttioJson: "No attio.json. Run this from your app project directory.",
|
|
5
|
-
invalidAttioJson: "Invalid attio.json",
|
|
6
|
-
writeError: "Error writing to attio.json",
|
|
7
|
-
};
|
|
8
|
-
function determineConfigPath() {
|
|
9
|
-
if (process.env.NODE_ENV === "development") {
|
|
10
|
-
const path = "./attio.dev.json";
|
|
11
|
-
if (existsSync(path))
|
|
12
|
-
return path;
|
|
13
|
-
}
|
|
14
|
-
const path = "./attio.json";
|
|
15
|
-
if (existsSync(path))
|
|
16
|
-
return path;
|
|
17
|
-
return ERRORS.noAttioJson;
|
|
18
|
-
}
|
|
19
|
-
export function loadAppConfigFile() {
|
|
20
|
-
const configPath = determineConfigPath();
|
|
21
|
-
if (configPath === ERRORS.noAttioJson)
|
|
22
|
-
return configPath;
|
|
23
|
-
let doc;
|
|
24
|
-
try {
|
|
25
|
-
doc = JSON.parse(readFileSync(configPath, "utf8"));
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
return ERRORS.invalidAttioJson;
|
|
29
|
-
}
|
|
30
|
-
try {
|
|
31
|
-
return parseConfig(doc);
|
|
32
|
-
}
|
|
33
|
-
catch (e) {
|
|
34
|
-
return e.message;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
export function updateAppConfig(mutator) {
|
|
38
|
-
const configPath = determineConfigPath();
|
|
39
|
-
if (configPath === ERRORS.noAttioJson)
|
|
40
|
-
return configPath;
|
|
41
|
-
const config = loadAppConfigFile();
|
|
42
|
-
if (typeof config === "string")
|
|
43
|
-
return config;
|
|
44
|
-
const updated = parseConfig(mutator(config));
|
|
45
|
-
try {
|
|
46
|
-
writeFileSync(configPath, JSON.stringify(updated, null, 2));
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
return ERRORS.writeError;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
-
import { stringify, parse } from "ini";
|
|
3
|
-
import { homedir } from "os";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import { createDeveloperAccount } from "../api/create-developer-account.js";
|
|
6
|
-
import { isValidSlug } from "./validate-slug.js";
|
|
7
|
-
export const initialDeveloperConfigSchema = z.object({
|
|
8
|
-
token: z.string(),
|
|
9
|
-
developer_slug: z.string(),
|
|
10
|
-
target_workspace_id: z.string().uuid().optional().nullable(),
|
|
11
|
-
});
|
|
12
|
-
const developerConfigSchema = initialDeveloperConfigSchema.extend({
|
|
13
|
-
developer_account_id: z.string(),
|
|
14
|
-
developer_account_member_id: z.string(),
|
|
15
|
-
});
|
|
16
|
-
export const configFileName = process.env.NODE_ENV === "development" ? ".attiorc.dev" : ".attiorc";
|
|
17
|
-
function determineDeveloperConfigPath() {
|
|
18
|
-
if (process.env.NODE_ENV === "development") {
|
|
19
|
-
const path = `${homedir()}/.attiorc.dev`;
|
|
20
|
-
if (existsSync(path))
|
|
21
|
-
return path;
|
|
22
|
-
}
|
|
23
|
-
const path = `${homedir()}/.attiorc`;
|
|
24
|
-
if (existsSync(path))
|
|
25
|
-
return path;
|
|
26
|
-
return "No config file";
|
|
27
|
-
}
|
|
28
|
-
function readDeveloperConfigFile() {
|
|
29
|
-
const path = determineDeveloperConfigPath();
|
|
30
|
-
if (path === "No config file")
|
|
31
|
-
return "No config file";
|
|
32
|
-
const configFileContents = readFileSync(path, { encoding: "utf8" });
|
|
33
|
-
try {
|
|
34
|
-
return { path, contents: parse(configFileContents) };
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return "Invalid config file";
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
export function loadInitialDeveloperConfig() {
|
|
41
|
-
const configFile = readDeveloperConfigFile();
|
|
42
|
-
switch (configFile) {
|
|
43
|
-
case "No config file":
|
|
44
|
-
case "Invalid config file":
|
|
45
|
-
return configFile;
|
|
46
|
-
}
|
|
47
|
-
const parsed = initialDeveloperConfigSchema.safeParse(configFile.contents);
|
|
48
|
-
if (!parsed.success)
|
|
49
|
-
return "Invalid config file";
|
|
50
|
-
parsed.data.target_workspace_id = null;
|
|
51
|
-
return parsed.data;
|
|
52
|
-
}
|
|
53
|
-
export async function loadDeveloperConfig() {
|
|
54
|
-
const configFile = readDeveloperConfigFile();
|
|
55
|
-
switch (configFile) {
|
|
56
|
-
case "No config file":
|
|
57
|
-
case "Invalid config file":
|
|
58
|
-
return configFile;
|
|
59
|
-
}
|
|
60
|
-
const parsed = developerConfigSchema.safeParse(configFile.contents);
|
|
61
|
-
if (parsed.success) {
|
|
62
|
-
if (!isValidSlug(parsed.data.developer_slug)) {
|
|
63
|
-
return "Invalid developer_slug: must be lower kebab case and contain only letters, numbers and hyphens. e.g. my-slug";
|
|
64
|
-
}
|
|
65
|
-
return parsed.data;
|
|
66
|
-
}
|
|
67
|
-
const initialDeveloperConfig = loadInitialDeveloperConfig();
|
|
68
|
-
if (typeof initialDeveloperConfig === "string")
|
|
69
|
-
return initialDeveloperConfig;
|
|
70
|
-
const { token, developer_slug: developerSlug } = initialDeveloperConfig;
|
|
71
|
-
if (!isValidSlug(developerSlug)) {
|
|
72
|
-
return "Invalid developer_slug: must be lower kebab case and contain only letters, numbers and hyphens. e.g. my-slug";
|
|
73
|
-
}
|
|
74
|
-
const devAccount = await createDeveloperAccount({ token, developerSlug });
|
|
75
|
-
const config = developerConfigSchema.parse({
|
|
76
|
-
token,
|
|
77
|
-
developer_slug: developerSlug,
|
|
78
|
-
developer_account_id: devAccount.developer_account.developer_account_id,
|
|
79
|
-
developer_account_member_id: devAccount.developer_account_member.developer_account_member_id,
|
|
80
|
-
target_workspace_id: initialDeveloperConfig.target_workspace_id ?? null,
|
|
81
|
-
});
|
|
82
|
-
writeFileSync(configFile.path, stringify(config));
|
|
83
|
-
return config;
|
|
84
|
-
}
|
|
85
|
-
export async function saveTargetWorkspaceToConfig(workspaceId) {
|
|
86
|
-
const configFile = readDeveloperConfigFile();
|
|
87
|
-
if (configFile === "No config file" || configFile === "Invalid config file")
|
|
88
|
-
return;
|
|
89
|
-
const config = developerConfigSchema.parse(configFile.contents);
|
|
90
|
-
config.target_workspace_id = workspaceId;
|
|
91
|
-
writeFileSync(configFile.path, stringify(config));
|
|
92
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { stringify } from "ini";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import { APP, APP_NO_PROTOCOL, IS_DEV } from "../env.js";
|
|
4
|
-
import { configFileName } from "../util/load-developer-config.js";
|
|
5
|
-
const exampleConfig = {
|
|
6
|
-
token: "YOUR TOKEN HERE",
|
|
7
|
-
developer_slug: "ANY UNIQUE SLUG YOU WANT",
|
|
8
|
-
target_workspace_id: "YOUR WORKSPACE ID HERE",
|
|
9
|
-
};
|
|
10
|
-
export function printInstallInstructions(reason) {
|
|
11
|
-
const write = (str = "") => process.stdout.write(str + "\n");
|
|
12
|
-
if (reason) {
|
|
13
|
-
write("Failed to load config file.");
|
|
14
|
-
write(chalk.red(reason));
|
|
15
|
-
write();
|
|
16
|
-
}
|
|
17
|
-
write("You will need to:");
|
|
18
|
-
write();
|
|
19
|
-
write(` 1. Log into${IS_DEV ? ` ${chalk.italic("DEVELOPMENT")}` : ""} Attio web app: ${APP}`);
|
|
20
|
-
write(` 2. Open Dev Tools > Application > Cookies > ${APP_NO_PROTOCOL}`);
|
|
21
|
-
write(` 3. Copy the value for attio-session. That's your "token".`);
|
|
22
|
-
write(` 4. Create a file in your home directory called ~/${configFileName}`);
|
|
23
|
-
write(` 5. It should have the following [INI] format:`);
|
|
24
|
-
write();
|
|
25
|
-
write(" " + stringify(exampleConfig).trim().replace(/\n/g, "\n "));
|
|
26
|
-
write();
|
|
27
|
-
write(" 6. Run this command again.");
|
|
28
|
-
write();
|
|
29
|
-
write(" ...");
|
|
30
|
-
write();
|
|
31
|
-
write(" N. PROFIT!");
|
|
32
|
-
}
|