appwrite-cli 6.0.0-rc.1 → 6.0.0-rc.3

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.
@@ -1,10 +1,8 @@
1
1
  const Tail = require('tail').Tail;
2
- const EventEmitter = require('node:events');
2
+ const chalk = require('chalk');
3
3
  const ignore = require("ignore");
4
4
  const tar = require("tar");
5
5
  const fs = require("fs");
6
- const ID = require("../id");
7
- const childProcess = require('child_process');
8
6
  const chokidar = require('chokidar');
9
7
  const inquirer = require("inquirer");
10
8
  const path = require("path");
@@ -12,13 +10,11 @@ const { Command } = require("commander");
12
10
  const { localConfig, globalConfig } = require("../config");
13
11
  const { paginate } = require('../paginate');
14
12
  const { functionsListVariables } = require('./functions');
15
- const { usersGet, usersCreateJWT } = require('./users');
16
- const { projectsCreateJWT } = require('./projects');
17
13
  const { questionsRunFunctions } = require("../questions");
18
- const { actionRunner, success, log, error, commandDescriptions, drawTable } = require("../parser");
14
+ const { actionRunner, success, log, warn, error, hint, commandDescriptions, drawTable } = require("../parser");
19
15
  const { systemHasCommand, isPortTaken, getAllFiles } = require('../utils');
20
- const { openRuntimesVersion, runtimeNames, systemTools, JwtManager, Queue } = require('../emulation/utils');
21
- const { dockerStop, dockerCleanup, dockerStart, dockerBuild, dockerPull, dockerStopActive } = require('../emulation/docker');
16
+ const { runtimeNames, systemTools, JwtManager, Queue } = require('../emulation/utils');
17
+ const { dockerStop, dockerCleanup, dockerStart, dockerBuild, dockerPull } = require('../emulation/docker');
22
18
 
23
19
  const runFunction = async ({ port, functionId, noVariables, noReload, userId } = {}) => {
24
20
  // Selection
@@ -68,7 +64,7 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
68
64
  }
69
65
 
70
66
  if(!portFound) {
71
- error('Could not find an available port. Please select a port with `appwrite run --port YOUR_PORT` command.');
67
+ error("Could not find an available port. Please select a port with 'appwrite run --port YOUR_PORT' command.");
72
68
  return;
73
69
  }
74
70
  }
@@ -86,16 +82,16 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
86
82
  commands: func.commands,
87
83
  };
88
84
 
89
- log("Local function configuration:");
90
85
  drawTable([settings]);
91
- log('If you wish to change your local settings, update the appwrite.json file and rerun the `appwrite run` command.');
86
+ log("If you wish to change your local settings, update the appwrite.json file and rerun the 'appwrite run' command.");
87
+ hint("Permissions, events, CRON and timeouts dont apply when running locally.");
92
88
 
93
- await dockerCleanup();
89
+ await dockerCleanup(func.$id);
94
90
 
95
91
  process.on('SIGINT', async () => {
96
92
  log('Cleaning up ...');
97
- await dockerCleanup();
98
- success();
93
+ await dockerCleanup(func.$id);
94
+ success("Local function successfully stopped.");
99
95
  process.exit();
100
96
  });
101
97
 
@@ -116,22 +112,17 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
116
112
 
117
113
  const variables = {};
118
114
  if(!noVariables) {
119
- if (globalConfig.getEndpoint() === '' || globalConfig.getCookie() === '') {
120
- error("No user is signed in. To sign in, run: appwrite login. Function will run locally, but will not have your function's environment variables set.");
121
- } else {
122
- try {
123
- const { variables: remoteVariables } = await paginate(functionsListVariables, {
124
- functionId: func['$id'],
125
- parseOutput: false
126
- }, 100, 'variables');
127
-
128
- remoteVariables.forEach((v) => {
129
- variables[v.key] = v.value;
130
- });
131
- } catch(err) {
132
- log("Could not fetch remote variables: " + err.message);
133
- log("Function will run locally, but will not have your function's environment variables set.");
134
- }
115
+ try {
116
+ const { variables: remoteVariables } = await paginate(functionsListVariables, {
117
+ functionId: func['$id'],
118
+ parseOutput: false
119
+ }, 100, 'variables');
120
+
121
+ remoteVariables.forEach((v) => {
122
+ variables[v.key] = v.value;
123
+ });
124
+ } catch(err) {
125
+ warn("Remote variables not fetched. Production environment variables will not be avaiable. Reason: " + err.message);
135
126
  }
136
127
  }
137
128
 
@@ -143,7 +134,11 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
143
134
  variables['APPWRITE_FUNCTION_RUNTIME_NAME'] = runtimeNames[runtimeName] ?? '';
144
135
  variables['APPWRITE_FUNCTION_RUNTIME_VERSION'] = func.runtime;
145
136
 
146
- await JwtManager.setup(userId);
137
+ try {
138
+ await JwtManager.setup(userId, func.scopes ?? []);
139
+ } catch(err) {
140
+ warn("Dynamic API key not generated. Header x-appwrite-key will not be set. Reason: " + err.message);
141
+ }
147
142
 
148
143
  const headers = {};
149
144
  headers['x-appwrite-key'] = JwtManager.functionJwt ?? '';
@@ -154,14 +149,12 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
154
149
  variables['OPEN_RUNTIMES_HEADERS'] = JSON.stringify(headers);
155
150
 
156
151
  await dockerPull(func);
157
- await dockerBuild(func, variables);
158
- await dockerStart(func, variables, port);
159
152
 
160
153
  new Tail(logsPath).on("line", function(data) {
161
- console.log(data);
154
+ process.stdout.write(chalk.white(`${data}\n`));
162
155
  });
163
156
  new Tail(errorsPath).on("line", function(data) {
164
- console.log(data);
157
+ process.stdout.write(chalk.white(`${data}\n`));
165
158
  });
166
159
 
167
160
  if(!noReload) {
@@ -177,23 +170,22 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
177
170
  Queue.events.on('reload', async ({ files }) => {
178
171
  Queue.lock();
179
172
 
180
- log('Live-reloading due to file changes: ');
181
- for(const file of files) {
182
- log(`- ${file}`);
183
- }
184
-
185
173
  try {
186
- log('Stopping the function ...');
187
-
188
- await dockerStopActive();
174
+ await dockerStop(func.$id);
189
175
 
190
176
  const dependencyFile = files.find((filePath) => tool.dependencyFiles.includes(filePath));
191
177
  if(tool.isCompiled || dependencyFile) {
192
- log(`Rebuilding the function due to cange in ${dependencyFile} ...`);
178
+ log(`Rebuilding the function due to file changes ...`);
193
179
  await dockerBuild(func, variables);
180
+
181
+ if(!Queue.isEmpty()) {
182
+ Queue.unlock();
183
+ return;
184
+ }
185
+
194
186
  await dockerStart(func, variables, port);
195
187
  } else {
196
- log('Hot-swapping function files ...');
188
+ log('Hot-swapping function.. Files with change are ' + files.join(', '));
197
189
 
198
190
  const functionPath = path.join(process.cwd(), func.path);
199
191
  const hotSwapPath = path.join(functionPath, '.appwrite/hot-swap');
@@ -255,6 +247,22 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
255
247
  Queue.unlock();
256
248
  }
257
249
  });
250
+
251
+ Queue.lock();
252
+
253
+ log('Building function using Docker ...');
254
+ await dockerBuild(func, variables);
255
+
256
+ if(!Queue.isEmpty()) {
257
+ Queue.unlock();
258
+ return;
259
+ }
260
+
261
+ log('Starting function using Docker ...');
262
+ hint('Function automatically restarts when you edit your code.');
263
+ await dockerStart(func, variables, port);
264
+
265
+ Queue.unlock();
258
266
  }
259
267
 
260
268
  const run = new Command("run")
package/lib/config.js CHANGED
@@ -4,6 +4,59 @@ const _path = require("path");
4
4
  const process = require("process");
5
5
  const JSONbig = require("json-bigint")({ storeAsString: false });
6
6
 
7
+ const KeysFunction = new Set(["path", "$id", "execute", "name", "enabled", "logging", "runtime", "scopes", "events", "schedule", "timeout", "entrypoint", "commands"]);
8
+ const KeysDatabase = new Set(["$id", "name", "enabled"]);
9
+ const KeysCollection = new Set(["$id", "$permissions", "databaseId", "name", "enabled", "documentSecurity", "attributes", "indexes"]);
10
+ const KeysStorage = new Set(["$id", "$permissions", "fileSecurity", "name", "enabled", "maximumFileSize", "allowedFileExtensions", "compression", "encryption", "antivirus"]);
11
+ const KeyTopics = new Set(["$id", "name", "subscribe"]);
12
+ const KeyAttributes = new Set([
13
+ "key",
14
+ "type",
15
+ "required",
16
+ "array",
17
+ "size",
18
+ "default",
19
+ // integer and float
20
+ "min",
21
+ "max",
22
+ // email, enum, URL, IP, and datetime
23
+ "format",
24
+ // enum
25
+ "elements",
26
+ // relationship
27
+ "relatedCollection",
28
+ "relationType",
29
+ "twoWay",
30
+ "twoWayKey",
31
+ "onDelete",
32
+ "side"
33
+ ]);
34
+ const KeyIndexes = new Set(["key", "type", "status", "attributes", "orders"]);
35
+
36
+ function whitelistKeys(value, keys, nestedKeys = {}) {
37
+ if(Array.isArray(value)) {
38
+ const newValue = [];
39
+
40
+ for(const item of value) {
41
+ newValue.push(whitelistKeys(item, keys, nestedKeys));
42
+ }
43
+
44
+ return newValue;
45
+ }
46
+
47
+ const newValue = {};
48
+ Object.keys(value).forEach((key) => {
49
+ if(keys.has(key)) {
50
+ if(nestedKeys[key]) {
51
+ newValue[key] = whitelistKeys(value[key], nestedKeys[key]);
52
+ } else {
53
+ newValue[key] = value[key];
54
+ }
55
+ }
56
+ });
57
+ return newValue;
58
+ }
59
+
7
60
  class Config {
8
61
  constructor(path) {
9
62
  this.path = path;
@@ -94,6 +147,8 @@ class Local extends Config {
94
147
  }
95
148
 
96
149
  addFunction(props) {
150
+ props = whitelistKeys(props, KeysFunction);
151
+
97
152
  if (!this.has("functions")) {
98
153
  this.set("functions", []);
99
154
  }
@@ -101,21 +156,6 @@ class Local extends Config {
101
156
  let functions = this.get("functions");
102
157
  for (let i = 0; i < functions.length; i++) {
103
158
  if (functions[i]['$id'] == props['$id']) {
104
- return;
105
- }
106
- }
107
- functions.push(props);
108
- this.set("functions", functions);
109
- }
110
-
111
- updateFunction(id, props) {
112
- if (!this.has("functions")) {
113
- return;
114
- }
115
-
116
- let functions = this.get("functions");
117
- for (let i = 0; i < functions.length; i++) {
118
- if (functions[i]['$id'] == id) {
119
159
  functions[i] = {
120
160
  ...functions[i],
121
161
  ...props
@@ -124,6 +164,9 @@ class Local extends Config {
124
164
  return;
125
165
  }
126
166
  }
167
+
168
+ functions.push(props);
169
+ this.set("functions", functions);
127
170
  }
128
171
 
129
172
  getCollections() {
@@ -149,6 +192,11 @@ class Local extends Config {
149
192
  }
150
193
 
151
194
  addCollection(props) {
195
+ props = whitelistKeys(props, KeysCollection, {
196
+ attributes: KeyAttributes,
197
+ indexes: KeyIndexes
198
+ });
199
+
152
200
  if (!this.has("collections")) {
153
201
  this.set("collections", []);
154
202
  }
@@ -188,6 +236,8 @@ class Local extends Config {
188
236
  }
189
237
 
190
238
  addBucket(props) {
239
+ props = whitelistKeys(props, KeysStorage);
240
+
191
241
  if (!this.has("buckets")) {
192
242
  this.set("buckets", []);
193
243
  }
@@ -227,6 +277,8 @@ class Local extends Config {
227
277
  }
228
278
 
229
279
  addMessagingTopic(props) {
280
+ props = whitelistKeys(props, KeyTopics);
281
+
230
282
  if (!this.has("topics")) {
231
283
  this.set("topics", []);
232
284
  }
@@ -266,6 +318,8 @@ class Local extends Config {
266
318
  }
267
319
 
268
320
  addDatabase(props) {
321
+ props = whitelistKeys(props, KeysDatabase);
322
+
269
323
  if (!this.has("databases")) {
270
324
  this.set("databases", []);
271
325
  }
@@ -329,7 +383,7 @@ class Local extends Config {
329
383
  return {
330
384
  projectId: this.get("projectId"),
331
385
  projectName: this.get("projectName"),
332
- projectSettings: this.get('projectSettings')
386
+ projectSettings: this.get('settings')
333
387
  };
334
388
  }
335
389
 
@@ -357,7 +411,6 @@ class Local extends Config {
357
411
  functions: projectSettings.serviceStatusForFunctions,
358
412
  graphql: projectSettings.serviceStatusForGraphql,
359
413
  messaging: projectSettings.serviceStatusForMessaging,
360
-
361
414
  },
362
415
  auth: {
363
416
  methods: {
@@ -380,7 +433,7 @@ class Local extends Config {
380
433
  }
381
434
  };
382
435
 
383
- this.set('projectSettings', settings)
436
+ this.set('settings', settings)
384
437
  }
385
438
 
386
439
  }
@@ -520,7 +573,7 @@ class Global extends Config {
520
573
  const current = this.getCurrentSession();
521
574
 
522
575
  if (current) {
523
- const config = this.get(current);
576
+ const config = this.get(current) ?? {};
524
577
 
525
578
  return config[key] !== undefined;
526
579
  }
@@ -530,7 +583,7 @@ class Global extends Config {
530
583
  const current = this.getCurrentSession();
531
584
 
532
585
  if (current) {
533
- const config = this.get(current);
586
+ const config = this.get(current) ?? {};
534
587
 
535
588
  return config[key];
536
589
  }
@@ -1,45 +1,43 @@
1
+ const chalk = require('chalk');
1
2
  const childProcess = require('child_process');
2
3
  const { localConfig } = require("../config");
3
4
  const path = require('path');
4
5
  const fs = require('fs');
5
- const { log,success } = require("../parser");
6
- const { openRuntimesVersion, systemTools } = require("./utils");
7
- const ID = require("../id");
8
-
9
- const activeDockerIds = {};
6
+ const { log, success, hint } = require("../parser");
7
+ const { openRuntimesVersion, systemTools, Queue } = require("./utils");
10
8
 
11
9
  async function dockerStop(id) {
12
- delete activeDockerIds[id];
13
10
  const stopProcess = childProcess.spawn('docker', ['rm', '--force', id], {
14
11
  stdio: 'pipe',
12
+ env: {
13
+ ...process.env,
14
+ DOCKER_CLI_HINTS: 'false'
15
+ }
15
16
  });
16
17
 
17
18
  await new Promise((res) => { stopProcess.on('close', res) });
18
19
  }
19
20
 
20
21
  async function dockerPull(func) {
21
- log('Pulling Docker image of function runtime ...');
22
-
23
22
  const runtimeChunks = func.runtime.split("-");
24
23
  const runtimeVersion = runtimeChunks.pop();
25
24
  const runtimeName = runtimeChunks.join("-");
26
25
  const imageName = `openruntimes/${runtimeName}:${openRuntimesVersion}-${runtimeVersion}`;
27
26
 
27
+ log('Verifying Docker image ...');
28
+
28
29
  const pullProcess = childProcess.spawn('docker', ['pull', imageName], {
29
30
  stdio: 'pipe',
30
- pwd: path.join(process.cwd(), func.path)
31
- });
32
-
33
- pullProcess.stderr.on('data', (data) => {
34
- process.stderr.write(`\n${data}$ `);
31
+ env: {
32
+ ...process.env,
33
+ DOCKER_CLI_HINTS: 'false'
34
+ }
35
35
  });
36
36
 
37
37
  await new Promise((res) => { pullProcess.on('close', res) });
38
38
  }
39
39
 
40
40
  async function dockerBuild(func, variables) {
41
- log('Building function using Docker ...');
42
-
43
41
  const runtimeChunks = func.runtime.split("-");
44
42
  const runtimeVersion = runtimeChunks.pop();
45
43
  const runtimeName = runtimeChunks.join("-");
@@ -47,7 +45,7 @@ async function dockerBuild(func, variables) {
47
45
 
48
46
  const functionDir = path.join(process.cwd(), func.path);
49
47
 
50
- const id = ID.unique();
48
+ const id = func.$id;
51
49
 
52
50
  const params = [ 'run' ];
53
51
  params.push('--name', id);
@@ -65,19 +63,40 @@ async function dockerBuild(func, variables) {
65
63
 
66
64
  const buildProcess = childProcess.spawn('docker', params, {
67
65
  stdio: 'pipe',
68
- pwd: functionDir
66
+ pwd: functionDir,
67
+ env: {
68
+ ...process.env,
69
+ DOCKER_CLI_HINTS: 'false'
70
+ }
69
71
  });
70
72
 
71
73
  buildProcess.stdout.on('data', (data) => {
72
- process.stdout.write(`\n${data}`);
74
+ process.stdout.write(chalk.blackBright(`${data}\n`));
73
75
  });
74
76
 
75
77
  buildProcess.stderr.on('data', (data) => {
76
- process.stderr.write(`\n${data}`);
78
+ process.stderr.write(chalk.blackBright(`${data}\n`));
77
79
  });
78
80
 
81
+ const killInterval = setInterval(() => {
82
+ if(!Queue.isEmpty()) {
83
+ log('Cancelling build ...');
84
+ buildProcess.stdout.destroy();
85
+ buildProcess.stdin.destroy();
86
+ buildProcess.stderr.destroy();
87
+ buildProcess.kill("SIGKILL");
88
+ clearInterval(killInterval);
89
+ }
90
+ }, 100);
91
+
79
92
  await new Promise((res) => { buildProcess.on('close', res) });
80
93
 
94
+ clearInterval(interval);
95
+
96
+ if(!Queue.isEmpty()) {
97
+ return;
98
+ }
99
+
81
100
  const copyPath = path.join(process.cwd(), func.path, '.appwrite', 'build.tar.gz');
82
101
  const copyDir = path.dirname(copyPath);
83
102
  if (!fs.existsSync(copyDir)) {
@@ -86,19 +105,16 @@ async function dockerBuild(func, variables) {
86
105
 
87
106
  const copyProcess = childProcess.spawn('docker', ['cp', `${id}:/mnt/code/code.tar.gz`, copyPath], {
88
107
  stdio: 'pipe',
89
- pwd: functionDir
108
+ pwd: functionDir,
109
+ env: {
110
+ ...process.env,
111
+ DOCKER_CLI_HINTS: 'false'
112
+ }
90
113
  });
91
114
 
92
115
  await new Promise((res) => { copyProcess.on('close', res) });
93
116
 
94
- const cleanupProcess = childProcess.spawn('docker', ['rm', '--force', id], {
95
- stdio: 'pipe',
96
- pwd: functionDir
97
- });
98
-
99
- await new Promise((res) => { cleanupProcess.on('close', res) });
100
-
101
- delete activeDockerIds[id];
117
+ await dockerStop(id);
102
118
 
103
119
  const tempPath = path.join(process.cwd(), func.path, 'code.tar.gz');
104
120
  if (fs.existsSync(tempPath)) {
@@ -107,15 +123,6 @@ async function dockerBuild(func, variables) {
107
123
  }
108
124
 
109
125
  async function dockerStart(func, variables, port) {
110
- log('Starting function using Docker ...');
111
-
112
- log("Permissions, events, CRON and timeouts dont apply when running locally.");
113
-
114
- log('💡 Hint: Function automatically restarts when you edit your code.');
115
-
116
- success(`Visit http://localhost:${port}/ to execute your function.`);
117
-
118
-
119
126
  const runtimeChunks = func.runtime.split("-");
120
127
  const runtimeVersion = runtimeChunks.pop();
121
128
  const runtimeName = runtimeChunks.join("-");
@@ -125,11 +132,10 @@ async function dockerStart(func, variables, port) {
125
132
 
126
133
  const functionDir = path.join(process.cwd(), func.path);
127
134
 
128
- const id = ID.unique();
135
+ const id = func.$id;
129
136
 
130
137
  const params = [ 'run' ];
131
138
  params.push('--rm');
132
- params.push('-d');
133
139
  params.push('--name', id);
134
140
  params.push('-p', `${port}:3000`);
135
141
  params.push('-e', 'APPWRITE_ENV=development');
@@ -145,43 +151,45 @@ async function dockerStart(func, variables, port) {
145
151
  params.push('-v', `${functionDir}/.appwrite/build.tar.gz:/mnt/code/code.tar.gz:ro`);
146
152
  params.push(imageName, 'sh', '-c', `helpers/start.sh "${tool.startCommand}"`);
147
153
 
148
- childProcess.spawn('docker', params, {
154
+ const startProcess = childProcess.spawn('docker', params, {
149
155
  stdio: 'pipe',
150
- pwd: functionDir
156
+ pwd: functionDir,
157
+ env: {
158
+ ...process.env,
159
+ DOCKER_CLI_HINTS: 'false'
160
+ }
151
161
  });
152
162
 
153
- activeDockerIds[id] = true;
154
- }
163
+ startProcess.stdout.on('data', (data) => {
164
+ process.stdout.write(chalk.blackBright(data));
165
+ });
155
166
 
156
- async function dockerCleanup() {
157
- await dockerStop();
167
+ startProcess.stderr.on('data', (data) => {
168
+ process.stdout.write(chalk.blackBright(data));
169
+ });
158
170
 
159
- const functions = localConfig.getFunctions();
160
- for(const func of functions) {
161
- const appwritePath = path.join(process.cwd(), func.path, '.appwrite');
162
- if (fs.existsSync(appwritePath)) {
163
- fs.rmSync(appwritePath, { recursive: true, force: true });
164
- }
171
+ success(`Visit http://localhost:${port}/ to execute your function.`);
172
+ }
165
173
 
166
- const tempPath = path.join(process.cwd(), func.path, 'code.tar.gz');
167
- if (fs.existsSync(tempPath)) {
168
- fs.rmSync(tempPath, { force: true });
169
- }
174
+ async function dockerCleanup(functionId) {
175
+ await dockerStop(functionId);
176
+
177
+ const func = localConfig.getFunction(functionId);
178
+ const appwritePath = path.join(process.cwd(), func.path, '.appwrite');
179
+ if (fs.existsSync(appwritePath)) {
180
+ fs.rmSync(appwritePath, { recursive: true, force: true });
170
181
  }
171
- }
172
182
 
173
- async function dockerStopActive() {
174
- const ids = Object.keys(activeDockerIds);
175
- for await (const id of ids) {
176
- await dockerStop(id);
183
+ const tempPath = path.join(process.cwd(), func.path, 'code.tar.gz');
184
+ if (fs.existsSync(tempPath)) {
185
+ fs.rmSync(tempPath, { force: true });
177
186
  }
178
187
  }
179
188
 
180
189
  module.exports = {
181
- dockerStop,
182
190
  dockerPull,
183
191
  dockerBuild,
184
192
  dockerStart,
185
193
  dockerCleanup,
186
- dockerStopActive,
194
+ dockerStop,
187
195
  }
@@ -1,9 +1,10 @@
1
1
  const EventEmitter = require('node:events');
2
2
  const { projectsCreateJWT } = require('../commands/projects');
3
3
  const { localConfig } = require("../config");
4
+ const { usersGet, usersCreateJWT } = require("../commands/users");
5
+ const { log } = require("../parser");
4
6
 
5
-
6
- const openRuntimesVersion = 'v3';
7
+ const openRuntimesVersion = 'v4';
7
8
 
8
9
  const runtimeNames = {
9
10
  'node': 'Node.js',
@@ -17,7 +18,8 @@ const runtimeNames = {
17
18
  'java': 'Java',
18
19
  'swift': 'Swift',
19
20
  'kotlin': 'Kotlin',
20
- 'bun': 'Bun'
21
+ 'bun': 'Bun',
22
+ 'go': 'Go',
21
23
  };
22
24
 
23
25
  const systemTools = {
@@ -81,6 +83,11 @@ const systemTools = {
81
83
  startCommand: "bun src/server.ts",
82
84
  dependencyFiles: [ "package.json", "package-lock.json", "bun.lockb" ]
83
85
  },
86
+ 'go': {
87
+ isCompiled: true,
88
+ startCommand: "src/function/server",
89
+ dependencyFiles: [ ]
90
+ },
84
91
  };
85
92
 
86
93
  const JwtManager = {
@@ -90,7 +97,7 @@ const JwtManager = {
90
97
  timerWarn: null,
91
98
  timerError: null,
92
99
 
93
- async setup(userId = null) {
100
+ async setup(userId = null, projectScopes = []) {
94
101
  if(this.timerWarn) {
95
102
  clearTimeout(this.timerWarn);
96
103
  }
@@ -123,8 +130,7 @@ const JwtManager = {
123
130
 
124
131
  const functionResponse = await projectsCreateJWT({
125
132
  projectId: localConfig.getProject().projectId,
126
- // TODO: Once we have endpoint for this, use it
127
- scopes: ["sessions.write","users.read","users.write","teams.read","teams.write","databases.read","databases.write","collections.read","collections.write","attributes.read","attributes.write","indexes.read","indexes.write","documents.read","documents.write","files.read","files.write","buckets.read","buckets.write","functions.read","functions.write","execution.read","execution.write","locale.read","avatars.read","health.read","providers.read","providers.write","messages.read","messages.write","topics.read","topics.write","subscribers.read","subscribers.write","targets.read","targets.write","rules.read","rules.write","migrations.read","migrations.write","vcs.read","vcs.write","assistant.read"],
133
+ scopes: projectScopes,
128
134
  duration: 60*60,
129
135
  parseOutput: false
130
136
  });
@@ -150,6 +156,9 @@ const Queue = {
150
156
  this.files = [];
151
157
  this.locked = true;
152
158
  },
159
+ isEmpty() {
160
+ return this.files.length === 0
161
+ },
153
162
  unlock() {
154
163
  this.locked = false;
155
164
  if(this.files.length > 0) {