appwrite-cli 17.1.0 → 17.2.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.
Files changed (56) hide show
  1. package/.github/workflows/ci.yml +1 -1
  2. package/.github/workflows/publish.yml +1 -1
  3. package/CHANGELOG.md +14 -0
  4. package/README.md +2 -2
  5. package/bun.lock +783 -0
  6. package/cli.ts +14 -2
  7. package/dist/bundle-win-arm64.mjs +1137 -733
  8. package/dist/cli.cjs +1137 -733
  9. package/dist/index.cjs +193 -79
  10. package/dist/index.js +193 -79
  11. package/dist/lib/client.d.ts +9 -0
  12. package/dist/lib/client.d.ts.map +1 -1
  13. package/dist/lib/commands/init.d.ts.map +1 -1
  14. package/dist/lib/constants.d.ts +1 -1
  15. package/dist/lib/emulation/docker.d.ts.map +1 -1
  16. package/dist/lib/parser.d.ts.map +1 -1
  17. package/dist/lib/questions.d.ts.map +1 -1
  18. package/dist/lib/types.d.ts +2 -0
  19. package/dist/lib/types.d.ts.map +1 -1
  20. package/dist/lib/utils.d.ts +12 -0
  21. package/dist/lib/utils.d.ts.map +1 -1
  22. package/install.ps1 +2 -2
  23. package/install.sh +1 -1
  24. package/lib/client.ts +12 -0
  25. package/lib/commands/init.ts +109 -2
  26. package/lib/commands/services/account.ts +110 -55
  27. package/lib/commands/services/activities.ts +4 -2
  28. package/lib/commands/services/backups.ts +24 -12
  29. package/lib/commands/services/databases.ts +150 -75
  30. package/lib/commands/services/functions.ts +60 -30
  31. package/lib/commands/services/graphql.ts +4 -2
  32. package/lib/commands/services/health.ts +46 -23
  33. package/lib/commands/services/locale.ts +16 -8
  34. package/lib/commands/services/messaging.ts +96 -48
  35. package/lib/commands/services/migrations.ts +28 -14
  36. package/lib/commands/services/organizations.ts +76 -38
  37. package/lib/commands/services/project.ts +12 -6
  38. package/lib/commands/services/projects.ts +103 -51
  39. package/lib/commands/services/proxy.ts +16 -8
  40. package/lib/commands/services/sites.ts +58 -29
  41. package/lib/commands/services/storage.ts +30 -15
  42. package/lib/commands/services/tables-db.ts +148 -74
  43. package/lib/commands/services/teams.ts +28 -14
  44. package/lib/commands/services/tokens.ts +10 -5
  45. package/lib/commands/services/users.ts +88 -44
  46. package/lib/commands/services/vcs.ts +20 -10
  47. package/lib/commands/services/webhooks.ts +12 -6
  48. package/lib/constants.ts +1 -1
  49. package/lib/emulation/docker.ts +1 -0
  50. package/lib/parser.ts +279 -122
  51. package/lib/questions.ts +8 -3
  52. package/lib/sdks.ts +0 -1
  53. package/lib/types.ts +2 -0
  54. package/lib/utils.ts +234 -0
  55. package/package.json +1 -1
  56. package/scoop/appwrite.config.json +3 -3
package/lib/questions.ts CHANGED
@@ -227,14 +227,16 @@ export const questionsInitProject: Question[] = [
227
227
  name: "project",
228
228
  message: "What would you like to name your project?",
229
229
  default: "My Awesome Project",
230
- when: (answer: Answers) => whenOverride(answer) && answer.start !== "existing",
230
+ when: (answer: Answers) =>
231
+ whenOverride(answer) && answer.start !== "existing",
231
232
  },
232
233
  {
233
234
  type: "input",
234
235
  name: "id",
235
236
  message: "What ID would you like to have for your project?",
236
237
  default: "unique()",
237
- when: (answer: Answers) => whenOverride(answer) && answer.start !== "existing",
238
+ when: (answer: Answers) =>
239
+ whenOverride(answer) && answer.start !== "existing",
238
240
  },
239
241
  {
240
242
  type: "search-list",
@@ -251,7 +253,8 @@ export const questionsInitProject: Question[] = [
251
253
  ];
252
254
 
253
255
  const { projects } = await paginate(
254
- async (args) => (await getProjectsService()).list(args.queries as string[]),
256
+ async (args) =>
257
+ (await getProjectsService()).list(args.queries as string[]),
255
258
  { parseOutput: false },
256
259
  100,
257
260
  "projects",
@@ -301,6 +304,7 @@ export const questionsInitProject: Question[] = [
301
304
  }));
302
305
  },
303
306
  when: (answer: Answers) => {
307
+ if (!whenOverride(answer)) return false;
304
308
  if (answer.start === "existing") return false;
305
309
  return isCloud();
306
310
  },
@@ -900,6 +904,7 @@ export const questionsInitResources: Question[] = [
900
904
  choices: [
901
905
  { name: "Function", value: "function" },
902
906
  { name: "Site", value: "site" },
907
+ { name: "Skill", value: "skill" },
903
908
  { name: "Table", value: "table" },
904
909
  { name: "Bucket", value: "bucket" },
905
910
  { name: "Team", value: "team" },
package/lib/sdks.ts CHANGED
@@ -88,4 +88,3 @@ export const sdkForProject = async (): Promise<Client> => {
88
88
  `Session not found. Please run \`${EXECUTABLE_NAME} login\` to create a session.`,
89
89
  );
90
90
  };
91
-
package/lib/types.ts CHANGED
@@ -53,11 +53,13 @@ export interface CliConfig {
53
53
  verbose: boolean;
54
54
  json: boolean;
55
55
  raw: boolean;
56
+ showSecrets: boolean;
56
57
  force: boolean;
57
58
  all: boolean;
58
59
  ids: string[];
59
60
  report: boolean;
60
61
  reportData: Record<string, unknown>;
62
+ displayFields: string[];
61
63
  }
62
64
 
63
65
  export interface SessionData {
package/lib/utils.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import fs from "fs";
2
+ import os from "os";
2
3
  import path from "path";
3
4
  import net from "net";
4
5
  import childProcess from "child_process";
@@ -172,6 +173,239 @@ export function isCloud(): boolean {
172
173
  return hostname.endsWith("appwrite.io");
173
174
  }
174
175
 
176
+ // --- Agent Skills helpers ---
177
+
178
+ const SKILLS_REPO = "https://github.com/appwrite/agent-skills";
179
+
180
+ const LANGUAGE_MARKERS: Record<string, string[]> = {
181
+ typescript: [
182
+ "package.json",
183
+ "tsconfig.json",
184
+ "bun.lockb",
185
+ "yarn.lock",
186
+ "package-lock.json",
187
+ ],
188
+ python: ["requirements.txt", "pyproject.toml", "setup.py", "Pipfile"],
189
+ php: ["composer.json"],
190
+ dart: ["pubspec.yaml"],
191
+ swift: ["Package.swift", "*.xcodeproj"],
192
+ kotlin: ["build.gradle.kts", "build.gradle"],
193
+ go: ["go.mod"],
194
+ ruby: ["Gemfile"],
195
+ dotnet: ["*.csproj", "*.sln", "*.fsproj"],
196
+ };
197
+
198
+ export interface SkillInfo {
199
+ name: string;
200
+ description: string;
201
+ dirName: string;
202
+ }
203
+
204
+ const parseSkillFrontmatter = (
205
+ content: string,
206
+ ): { name: string; description: string } => {
207
+ const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
208
+ if (!match) return { name: "", description: "" };
209
+
210
+ const frontmatter = match[1];
211
+ const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
212
+ const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
213
+
214
+ return {
215
+ name: nameMatch ? nameMatch[1].trim() : "",
216
+ description: descMatch ? descMatch[1].trim() : "",
217
+ };
218
+ };
219
+
220
+ export function hasSkillsInstalled(configDirectoryPath: string): boolean {
221
+ const skillsDirs = [
222
+ path.join(configDirectoryPath, ".agents", "skills"),
223
+ path.join(configDirectoryPath, ".claude", "skills"),
224
+ ];
225
+ return skillsDirs.some(
226
+ (dir) =>
227
+ fs.existsSync(dir) &&
228
+ fs.statSync(dir).isDirectory() &&
229
+ fs.readdirSync(dir).length > 0,
230
+ );
231
+ }
232
+
233
+ export function fetchAvailableSkills(): {
234
+ skills: SkillInfo[];
235
+ tempDir: string;
236
+ } {
237
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "appwrite-skills-"));
238
+
239
+ let gitInitCommands = `git clone --single-branch --depth 1 --sparse ${SKILLS_REPO} .`;
240
+ let gitPullCommands = `git sparse-checkout add skills`;
241
+
242
+ if (process.platform === "win32") {
243
+ gitInitCommands = 'cmd /c "' + gitInitCommands + '"';
244
+ gitPullCommands = 'cmd /c "' + gitPullCommands + '"';
245
+ }
246
+
247
+ try {
248
+ childProcess.execSync(gitInitCommands, { stdio: "pipe", cwd: tempDir });
249
+ childProcess.execSync(gitPullCommands, { stdio: "pipe", cwd: tempDir });
250
+ } catch (err) {
251
+ fs.rmSync(tempDir, { recursive: true, force: true });
252
+ const errorMessage = err instanceof Error ? err.message : String(err);
253
+ if (errorMessage.includes("error: unknown option")) {
254
+ throw new Error(
255
+ `${errorMessage}\n\nSuggestion: Try updating your git to the latest version, then trying to run this command again.`,
256
+ );
257
+ } else if (
258
+ errorMessage.includes(
259
+ "is not recognized as an internal or external command,",
260
+ ) ||
261
+ errorMessage.includes("command not found")
262
+ ) {
263
+ throw new Error(
264
+ `${errorMessage}\n\nSuggestion: It appears that git is not installed, try installing git then trying to run this command again.`,
265
+ );
266
+ }
267
+ throw err;
268
+ }
269
+
270
+ const skillsSrcDir = path.join(tempDir, "skills");
271
+ if (!fs.existsSync(skillsSrcDir)) {
272
+ fs.rmSync(tempDir, { recursive: true, force: true });
273
+ throw new Error("No skills directory found in the repository.");
274
+ }
275
+
276
+ const skillDirs = fs
277
+ .readdirSync(skillsSrcDir, { withFileTypes: true })
278
+ .filter((entry) => entry.isDirectory());
279
+
280
+ const skills: SkillInfo[] = [];
281
+ for (const dir of skillDirs) {
282
+ const skillMdPath = path.join(skillsSrcDir, dir.name, "SKILL.md");
283
+ if (fs.existsSync(skillMdPath)) {
284
+ const content = fs.readFileSync(skillMdPath, "utf-8");
285
+ const { name, description } = parseSkillFrontmatter(content);
286
+ skills.push({
287
+ name: name || dir.name,
288
+ description,
289
+ dirName: dir.name,
290
+ });
291
+ }
292
+ }
293
+
294
+ if (skills.length === 0) {
295
+ fs.rmSync(tempDir, { recursive: true, force: true });
296
+ throw new Error("No skills found in the repository.");
297
+ }
298
+
299
+ return { skills, tempDir };
300
+ }
301
+
302
+ export function detectProjectSkills(
303
+ cwd: string,
304
+ skills: SkillInfo[],
305
+ ): SkillInfo[] {
306
+ const detected: Set<string> = new Set();
307
+
308
+ for (const [language, markers] of Object.entries(LANGUAGE_MARKERS)) {
309
+ for (const marker of markers) {
310
+ if (marker.includes("*")) {
311
+ const ext = marker.replace("*", "");
312
+ try {
313
+ const files = fs.readdirSync(cwd);
314
+ if (files.some((f) => f.endsWith(ext))) {
315
+ detected.add(language);
316
+ }
317
+ } catch {
318
+ // ignore read errors
319
+ }
320
+ } else if (fs.existsSync(path.join(cwd, marker))) {
321
+ detected.add(language);
322
+ }
323
+ }
324
+ }
325
+
326
+ // Always include the CLI skill
327
+ detected.add("cli");
328
+
329
+ return skills.filter((skill) =>
330
+ Array.from(detected).some((lang) =>
331
+ skill.dirName.toLowerCase().includes(lang),
332
+ ),
333
+ );
334
+ }
335
+
336
+ export function placeSkills(
337
+ cwd: string,
338
+ tempDir: string,
339
+ selectedDirNames: string[],
340
+ selectedAgents: string[],
341
+ useSymlinks: boolean,
342
+ ): void {
343
+ const skillsSrcDir = path.join(tempDir, "skills");
344
+
345
+ if (useSymlinks && selectedAgents.length > 1) {
346
+ const canonicalAgent = selectedAgents[0];
347
+ const canonicalBase = path.join(cwd, canonicalAgent, "skills");
348
+ fs.mkdirSync(canonicalBase, { recursive: true });
349
+
350
+ for (const dirName of selectedDirNames) {
351
+ const src = path.join(skillsSrcDir, dirName);
352
+ const dest = path.join(canonicalBase, dirName);
353
+ if (fs.existsSync(dest)) {
354
+ fs.rmSync(dest, { recursive: true, force: true });
355
+ }
356
+ fs.cpSync(src, dest, { recursive: true });
357
+ }
358
+
359
+ for (const agent of selectedAgents.slice(1)) {
360
+ const targetBase = path.join(cwd, agent, "skills");
361
+ fs.mkdirSync(targetBase, { recursive: true });
362
+
363
+ for (const dirName of selectedDirNames) {
364
+ const canonicalSkillDir = path.join(canonicalBase, dirName);
365
+ const dest = path.join(targetBase, dirName);
366
+
367
+ try {
368
+ fs.lstatSync(dest);
369
+ fs.rmSync(dest, { recursive: true, force: true });
370
+ } catch {
371
+ // dest does not exist, nothing to remove
372
+ }
373
+
374
+ const relativePath = path.relative(targetBase, canonicalSkillDir);
375
+ try {
376
+ fs.symlinkSync(relativePath, dest);
377
+ } catch (err: unknown) {
378
+ if (
379
+ process.platform === "win32" &&
380
+ (err as NodeJS.ErrnoException).code === "EPERM"
381
+ ) {
382
+ throw new Error(
383
+ "Symlinks require Developer Mode or Administrator rights on Windows.\n" +
384
+ "Enable Developer Mode in Settings > System > For developers, or re-run as Administrator.\n" +
385
+ "Alternatively, use 'Copy' install mode instead.",
386
+ );
387
+ }
388
+ throw err;
389
+ }
390
+ }
391
+ }
392
+ } else {
393
+ for (const agent of selectedAgents) {
394
+ const targetBase = path.join(cwd, agent, "skills");
395
+ fs.mkdirSync(targetBase, { recursive: true });
396
+
397
+ for (const dirName of selectedDirNames) {
398
+ const src = path.join(skillsSrcDir, dirName);
399
+ const dest = path.join(targetBase, dirName);
400
+ if (fs.existsSync(dest)) {
401
+ fs.rmSync(dest, { recursive: true, force: true });
402
+ }
403
+ fs.cpSync(src, dest, { recursive: true });
404
+ }
405
+ }
406
+ }
407
+ }
408
+
175
409
  export function arrayEqualsUnordered(left: unknown, right: unknown): boolean {
176
410
  const a = Array.isArray(left)
177
411
  ? [...left].map((item) => String(item)).sort()
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "type": "module",
4
4
  "homepage": "https://appwrite.io/support",
5
5
  "description": "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API",
6
- "version": "17.1.0",
6
+ "version": "17.2.1",
7
7
  "license": "BSD-3-Clause",
8
8
  "main": "dist/index.cjs",
9
9
  "module": "dist/index.js",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/ScoopInstaller/Scoop/master/schema.json",
3
- "version": "17.1.0",
3
+ "version": "17.2.1",
4
4
  "description": "The Appwrite CLI is a command-line application that allows you to interact with Appwrite and perform server-side tasks using your terminal.",
5
5
  "homepage": "https://github.com/appwrite/sdk-for-cli",
6
6
  "license": "BSD-3-Clause",
7
7
  "architecture": {
8
8
  "64bit": {
9
- "url": "https://github.com/appwrite/sdk-for-cli/releases/download/17.1.0/appwrite-cli-win-x64.exe",
9
+ "url": "https://github.com/appwrite/sdk-for-cli/releases/download/17.2.1/appwrite-cli-win-x64.exe",
10
10
  "bin": [
11
11
  [
12
12
  "appwrite-cli-win-x64.exe",
@@ -15,7 +15,7 @@
15
15
  ]
16
16
  },
17
17
  "arm64": {
18
- "url": "https://github.com/appwrite/sdk-for-cli/releases/download/17.1.0/appwrite-cli-win-arm64.exe",
18
+ "url": "https://github.com/appwrite/sdk-for-cli/releases/download/17.2.1/appwrite-cli-win-arm64.exe",
19
19
  "bin": [
20
20
  [
21
21
  "appwrite-cli-win-arm64.exe",