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.
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/publish.yml +1 -1
- package/CHANGELOG.md +14 -0
- package/README.md +2 -2
- package/bun.lock +783 -0
- package/cli.ts +14 -2
- package/dist/bundle-win-arm64.mjs +1137 -733
- package/dist/cli.cjs +1137 -733
- package/dist/index.cjs +193 -79
- package/dist/index.js +193 -79
- package/dist/lib/client.d.ts +9 -0
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/commands/init.d.ts.map +1 -1
- package/dist/lib/constants.d.ts +1 -1
- package/dist/lib/emulation/docker.d.ts.map +1 -1
- package/dist/lib/parser.d.ts.map +1 -1
- package/dist/lib/questions.d.ts.map +1 -1
- package/dist/lib/types.d.ts +2 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/utils.d.ts +12 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/install.ps1 +2 -2
- package/install.sh +1 -1
- package/lib/client.ts +12 -0
- package/lib/commands/init.ts +109 -2
- package/lib/commands/services/account.ts +110 -55
- package/lib/commands/services/activities.ts +4 -2
- package/lib/commands/services/backups.ts +24 -12
- package/lib/commands/services/databases.ts +150 -75
- package/lib/commands/services/functions.ts +60 -30
- package/lib/commands/services/graphql.ts +4 -2
- package/lib/commands/services/health.ts +46 -23
- package/lib/commands/services/locale.ts +16 -8
- package/lib/commands/services/messaging.ts +96 -48
- package/lib/commands/services/migrations.ts +28 -14
- package/lib/commands/services/organizations.ts +76 -38
- package/lib/commands/services/project.ts +12 -6
- package/lib/commands/services/projects.ts +103 -51
- package/lib/commands/services/proxy.ts +16 -8
- package/lib/commands/services/sites.ts +58 -29
- package/lib/commands/services/storage.ts +30 -15
- package/lib/commands/services/tables-db.ts +148 -74
- package/lib/commands/services/teams.ts +28 -14
- package/lib/commands/services/tokens.ts +10 -5
- package/lib/commands/services/users.ts +88 -44
- package/lib/commands/services/vcs.ts +20 -10
- package/lib/commands/services/webhooks.ts +12 -6
- package/lib/constants.ts +1 -1
- package/lib/emulation/docker.ts +1 -0
- package/lib/parser.ts +279 -122
- package/lib/questions.ts +8 -3
- package/lib/sdks.ts +0 -1
- package/lib/types.ts +2 -0
- package/lib/utils.ts +234 -0
- package/package.json +1 -1
- 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) =>
|
|
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) =>
|
|
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) =>
|
|
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
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
|
|
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
|
|
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
|
|
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
|
|
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",
|