@docyrus/animaguru 0.7.0 → 0.7.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/README.md CHANGED
@@ -72,6 +72,21 @@ Running `init` again refreshes an existing skill with the bundled version withou
72
72
 
73
73
  The default server is https://animaguru.com. Use `--url http://localhost:5190` on the leaf command or `ANIMAGURU_URL` to override it. HTTP is allowed only for loopback development. Authentication is stored separately per server in `~/.config/animaguru/credentials.json`, with file permissions 0600. Set `ANIMAGURU_CONFIG_DIR` for an isolated configuration directory. Browser login uses a ten-minute, single-use device code and receives a seven-day revocable Better Auth session. `auth login --no-browser` prints the URL for manual use. `auth logout` revokes the token. `ANIMAGURU_TOKEN` supports secret injection by agent runners; do not put tokens in arguments or commit them.
74
74
 
75
+ ## Personal and team projects
76
+
77
+ CLI 0.7.1 can select a cloud workspace for project listing, project creation, and credit balances:
78
+
79
+ ```sh
80
+ animaguru workspaces list
81
+ animaguru workspaces use team_0123456789abcdef0123456789abcdef
82
+ animaguru projects list
83
+ animaguru projects create --name "Shared cast" --style pixel
84
+ animaguru credits
85
+ animaguru workspaces use personal
86
+ ```
87
+
88
+ Use a team ID returned by `workspaces list`. The choice is saved per server in the existing credentials file. `workspaces list` also shows invitations; accept invitations in the web app before selecting that team. `projects get`, generation, jobs, and files use the project ID and work for any project you can access, regardless of the selected workspace. With `ANIMAGURU_TOKEN`, set `ANIMAGURU_WORKSPACE=personal` or a team ID instead of saving a selection. A cloud team is separate from the local `animaguru create workspace NAME` video folder.
89
+
75
90
  ## Agent workflow
76
91
 
77
92
  ```sh
package/dist/animaguru.js CHANGED
@@ -542,7 +542,7 @@ var init_dist = __esm({
542
542
  // cli/package.json
543
543
  var package_default = {
544
544
  name: "@docyrus/animaguru",
545
- version: "0.7.0",
545
+ version: "0.7.1",
546
546
  description: "Agent-friendly AnimaGuru CLI built with CrustJS",
547
547
  type: "module",
548
548
  bin: {
@@ -25871,6 +25871,22 @@ async function writeCredentials(credentials) {
25871
25871
  await chmod2(temp, 384);
25872
25872
  await rename2(temp, path);
25873
25873
  }
25874
+ async function selectedWorkspace(origin) {
25875
+ const saved = process.env.ANIMAGURU_TOKEN ? void 0 : (await readCredentials())[origin]?.workspaceId;
25876
+ const value = process.env.ANIMAGURU_WORKSPACE ?? saved ?? "me";
25877
+ if (value === "personal" || value === "me") return "me";
25878
+ if (!/^team_[a-f0-9]{32}$/.test(value)) throw new Error("ANIMAGURU_WORKSPACE must be 'personal' or a team ID.");
25879
+ return value;
25880
+ }
25881
+ async function saveWorkspace(origin, workspaceId) {
25882
+ if (process.env.ANIMAGURU_TOKEN) throw new Error("With ANIMAGURU_TOKEN, set ANIMAGURU_WORKSPACE instead of saving a workspace.");
25883
+ if (process.env.ANIMAGURU_WORKSPACE) throw new Error("Unset ANIMAGURU_WORKSPACE before changing the saved workspace.");
25884
+ const credentials = await readCredentials();
25885
+ const saved = credentials[origin];
25886
+ if (!saved || saved.expiresAt <= Date.now()) throw new Error("Sign in with animaguru auth login first.");
25887
+ credentials[origin] = { ...saved, workspaceId };
25888
+ await writeCredentials(credentials);
25889
+ }
25874
25890
  var ServiceError = class extends Error {
25875
25891
  constructor(status, code, message) {
25876
25892
  super(message);
@@ -27349,6 +27365,10 @@ var id = { name: "id", type: "string", required: true, description: "Project ID"
27349
27365
  var out = (stdout, data) => stdout(JSON.stringify(data));
27350
27366
  var actions = (text) => text ? text.split(",").map((s) => s.trim()).filter(Boolean) : [];
27351
27367
  var projectPath = (project) => `/api/v1/sprites/${encodeURIComponent(project)}`;
27368
+ var workspaceQuery = async (client) => {
27369
+ const selected = await selectedWorkspace(client.origin);
27370
+ return selected === "me" ? "" : `?workspace=${encodeURIComponent(selected)}`;
27371
+ };
27352
27372
  async function openBrowser(url2) {
27353
27373
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
27354
27374
  const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url2] : [url2];
@@ -27419,7 +27439,29 @@ Confirm code: ${device.user_code}`);
27419
27439
  delete credentials[client.origin];
27420
27440
  await writeCredentials(credentials);
27421
27441
  out(stdout, { authenticated: false });
27422
- })))).add(defineCommand("credits", { description: "Show your remaining credit balance" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request("/api/v1/credits"))))).add(defineCommand("catalog", { description: "List styles, kinds, actions, asset types, models, video settings and speech voices" }, (command) => command.action(({ stdout }) => out(stdout, { styles: SPRITE_STYLES, kinds: SPRITE_KINDS, actions: ALL_SPRITE_ACTIONS, imageModels: SPRITE_IMAGE_MODEL_IDS, videoModels: SPRITE_VIDEO_MODEL_IDS, assetTypes: ASSET_TYPES, musicModels: MUSIC_MODELS.map((m) => m.id), speechModels: SPEECH_MODELS.map((m) => m.id), sfxModels: SFX_MODELS.map((m) => m.id), elevenLabsVoices: ELEVENLABS_VOICES, voices: SPEECH_VOICES, videoProfiles: SPRITE_VIDEO_MODELS })))).command("projects", (projects) => projects.add(defineCommand("list", { description: "List your private projects" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request("/api/v1/projects"))))).add(defineCommand("create", { description: "Create an empty sprite project; generate artwork with jobs submit" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).flags(...modelFlags).flags({ name: "name", type: "string", required: true }, { name: "style", type: "string", required: true, choices: SPRITE_STYLES }).action(async ({ flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request("/api/v1/sprites", { method: "POST", body: { name: flags.name, style: flags.style, ...selectedModels(flags) } }))))).add(defineCommand("get", { description: "Read project, subjects, animations, estimates and job state" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(projectPath(args.id)))))).add(defineCommand("rename", { description: "Rename a project" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags({ name: "name", type: "string", required: true }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`/api/v1/projects/${encodeURIComponent(args.id)}`, { method: "PATCH", body: { name: flags.name } }))))).add(defineCommand("delete", { description: "Delete a project; requires --yes" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags({ name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
27442
+ })))).command("workspaces", (workspaces) => workspaces.add(defineCommand("list", { description: "List Personal, joined teams and pending invitations" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => {
27443
+ const client = new Client(flags.url, signal);
27444
+ const data = await client.request("/api/v1/workspaces");
27445
+ const selected = await selectedWorkspace(client.origin);
27446
+ out(stdout, { ...data, current: data.workspaces.find((item) => selected === "me" ? item.type === "user" : item.id === selected) ?? null });
27447
+ }))).add(defineCommand("use", { description: "Select Personal or a joined team for credits and project creation/listing" }, (command) => command.args({ name: "id", type: "string", required: true, description: "'personal' or a team ID from workspaces list" }).flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ args, flags, stdout, signal }) => {
27448
+ const client = new Client(flags.url, signal);
27449
+ const { workspaces: workspaces2 } = await client.request("/api/v1/workspaces");
27450
+ const chosen = args.id === "personal" || args.id === "me" ? workspaces2.find((item) => item.type === "user") : workspaces2.find((item) => item.type === "team" && item.id === args.id);
27451
+ if (!chosen) throw new Error("Workspace not found in your account. Run animaguru workspaces list.");
27452
+ await saveWorkspace(client.origin, chosen.type === "user" ? "me" : chosen.id);
27453
+ out(stdout, { current: chosen });
27454
+ })))).add(defineCommand("credits", { description: "Show the selected workspace's remaining credit balance" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => {
27455
+ const client = new Client(flags.url, signal);
27456
+ out(stdout, await client.request(`/api/v1/credits${await workspaceQuery(client)}`));
27457
+ }))).add(defineCommand("catalog", { description: "List styles, kinds, actions, asset types, models, video settings and speech voices" }, (command) => command.action(({ stdout }) => out(stdout, { styles: SPRITE_STYLES, kinds: SPRITE_KINDS, actions: ALL_SPRITE_ACTIONS, imageModels: SPRITE_IMAGE_MODEL_IDS, videoModels: SPRITE_VIDEO_MODEL_IDS, assetTypes: ASSET_TYPES, musicModels: MUSIC_MODELS.map((m) => m.id), speechModels: SPEECH_MODELS.map((m) => m.id), sfxModels: SFX_MODELS.map((m) => m.id), elevenLabsVoices: ELEVENLABS_VOICES, voices: SPEECH_VOICES, videoProfiles: SPRITE_VIDEO_MODELS })))).command("projects", (projects) => projects.add(defineCommand("list", { description: "List projects in the selected workspace" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => {
27458
+ const client = new Client(flags.url, signal);
27459
+ out(stdout, await client.request(`/api/v1/projects${await workspaceQuery(client)}`));
27460
+ }))).add(defineCommand("create", { description: "Create an empty sprite project; generate artwork with jobs submit" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).flags(...modelFlags).flags({ name: "name", type: "string", required: true }, { name: "style", type: "string", required: true, choices: SPRITE_STYLES }).action(async ({ flags, stdout, signal }) => {
27461
+ const client = new Client(flags.url, signal);
27462
+ const selected = await selectedWorkspace(client.origin);
27463
+ out(stdout, await client.request("/api/v1/sprites", { method: "POST", body: { owner: selected, name: flags.name, style: flags.style, ...selectedModels(flags) } }));
27464
+ }))).add(defineCommand("get", { description: "Read project, subjects, animations, estimates and job state" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(projectPath(args.id)))))).add(defineCommand("rename", { description: "Rename a project" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags({ name: "name", type: "string", required: true }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`/api/v1/projects/${encodeURIComponent(args.id)}`, { method: "PATCH", body: { name: flags.name } }))))).add(defineCommand("delete", { description: "Delete a project; requires --yes" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags({ name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
27423
27465
  if (!flags.yes) throw new Error("Pass --yes to delete this project.");
27424
27466
  out(stdout, await new Client(flags.url, signal).request(`/api/v1/projects/${encodeURIComponent(args.id)}`, { method: "DELETE" }));
27425
27467
  })))).command("assets", (assets) => assets.add(defineCommand("list", { description: "List saved assets, their blob hashes, and configured generation models" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${projectPath(args.id)}/assets`))))).add(defineCommand("generate", { description: "Generate an image, video, music, speech or sound-effect asset; --yes authorizes credits" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docyrus/animaguru",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Agent-friendly AnimaGuru CLI built with CrustJS",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@ Before creative work in a multi-animation workspace, select or create the intend
16
16
  Share the [animation-style gallery](https://animaguru.com/animation-styles), or open that URL in the internal browser when available, while asking the user to choose a style. Read the installed `animaguru.templates.json` for available styles and their skill names; `animaguru templates` lists the current CLI catalog. Offer the local catalog's choices so users select a skill actually installed in their workspace. Templates define appearance and motion; derive audience, message and story from the brief. Local scaffolding is separate from `projects create`, which creates a remote asset project. Never run `create` over an existing workspace.
17
17
 
18
18
  - Run `animaguru schema` for the installed command structure and flags, and `animaguru catalog` for styles, subject kinds, actions, asset types, models, video settings, and speech voices. Use `--help` on a leaf command for details.
19
- - Check `animaguru auth status` and `animaguru credits` before generation. If sign-in is needed, run `animaguru auth login --no-browser` and let the user approve the printed browser URL. Do not read or print stored credentials. Agent runners may inject `ANIMAGURU_TOKEN` as a secret.
19
+ - Check `animaguru auth status`, `animaguru workspaces list`, and `animaguru credits` before generation. If sign-in is needed, run `animaguru auth login --no-browser` and let the user approve the printed browser URL. Select the user's requested Personal or team workspace with `animaguru workspaces use personal|TEAM_ID` before listing or creating projects. Do not read or print stored credentials. Agent runners may inject `ANIMAGURU_TOKEN` and `ANIMAGURU_WORKSPACE` as secrets or configuration.
20
20
  - The default service is `https://animaguru.com`. Respect an existing `ANIMAGURU_URL` or pass `--url` to the leaf command when the user chooses another service. HTTP is supported only for loopback development.
21
21
 
22
22
  ## Choose the project and submit work