@localess/cli 3.0.0 → 3.0.1-dev.20260325211243

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
@@ -249,6 +249,31 @@ const content = await client.getContentBySlug<Page>('home', { locale: 'en' });
249
249
 
250
250
  ---
251
251
 
252
+ ## AI Coding Agents
253
+
254
+ This package ships a [`SKILL.md`](./SKILL.md) file that provides AI coding agents (GitHub Copilot, Claude Code, Cursor, and others) with accurate, up-to-date APIs, patterns, and best practices. Most agents automatically read `SKILL.md` when starting a session.
255
+
256
+ ### Using SKILL.md in your project
257
+
258
+ `SKILL.md` is included in the npm package, so it is available locally after installation. Reference it from your project's `AGENTS.md` to ensure your agent reads accurate Localess documentation every session:
259
+
260
+ ```markdown
261
+ ## Localess
262
+
263
+ @node_modules/@localess/cli/SKILL.md
264
+ ```
265
+
266
+ The `@` prefix is the syntax used by most agent tools (GitHub Copilot, Claude Code, Cursor) to import file contents inline into the agent context.
267
+
268
+ When you change the public API of this package, update `SKILL.md` alongside your code:
269
+
270
+ - **New option or parameter** → add it to the relevant options table and usage example
271
+ - **Changed behaviour** → update the description and any affected code snippets
272
+ - **Deprecated API** → mark it clearly and point to the replacement
273
+ - **New command or subcommand** → add a full entry with all flags and examples
274
+
275
+ ---
276
+
252
277
  ## License
253
278
 
254
279
  [MIT](../../LICENSE)
package/SKILL.md ADDED
@@ -0,0 +1,363 @@
1
+ # SKILL: @localess/cli
2
+
3
+ ## Overview
4
+
5
+ `@localess/cli` is the **command-line interface** for the Localess headless CMS platform. It enables:
6
+
7
+ - Authenticating with a Localess instance
8
+ - Pushing and pulling translations (flat JSON; nested format supported for pull only)
9
+ - Generating TypeScript type definitions from the OpenAPI schema
10
+
11
+ **Status:** Early development (v3.0.0). Requires Node.js >= 20.0.0.
12
+
13
+ ---
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install -g @localess/cli
19
+ # or use npx
20
+ npx @localess/cli [command]
21
+ ```
22
+
23
+ ---
24
+
25
+ ## Authentication
26
+
27
+ ### Login
28
+
29
+ Authenticate and persist credentials locally. Interactive prompts fill in any missing options.
30
+
31
+ ```bash
32
+ localess login \
33
+ --origin https://my-localess.web.app \
34
+ --space YOUR_SPACE_ID \
35
+ --token YOUR_API_TOKEN
36
+ ```
37
+
38
+ **Options:**
39
+
40
+ | Flag | Description |
41
+ |------------------------|---------------------------------|
42
+ | `-o, --origin <url>` | Localess instance URL |
43
+ | `-s, --space <id>` | Space ID (from Space settings) |
44
+ | `-t, --token <token>` | API token (masked input prompt) |
45
+
46
+ **Behavior:**
47
+ 1. Checks for existing credentials (env vars or file)
48
+ 2. Prompts interactively for any missing options
49
+ 3. Validates credentials by calling the API
50
+ 4. Saves credentials to `.localess/credentials.json` (mode `0o600`)
51
+
52
+ ### Logout
53
+
54
+ ```bash
55
+ localess logout
56
+ ```
57
+
58
+ - Clears `.localess/credentials.json` (writes `{}`)
59
+ - If authenticated via environment variables, instructs you to unset them manually
60
+
61
+ ---
62
+
63
+ ## Credential Storage
64
+
65
+ Credentials are resolved in this priority order:
66
+
67
+ 1. **Environment variables** (highest priority)
68
+ 2. **`.localess/credentials.json`** (file-based)
69
+
70
+ ### Environment Variables
71
+
72
+ ```bash
73
+ export LOCALESS_ORIGIN=https://my-localess.web.app
74
+ export LOCALESS_SPACE=YOUR_SPACE_ID
75
+ export LOCALESS_TOKEN=YOUR_API_TOKEN
76
+ ```
77
+
78
+ Recommended for **CI/CD pipelines** — no `localess login` step needed.
79
+
80
+ ### File-based Credentials
81
+
82
+ ```json
83
+ // .localess/credentials.json
84
+ {
85
+ "origin": "https://my-localess.web.app",
86
+ "space": "YOUR_SPACE_ID",
87
+ "token": "YOUR_API_TOKEN"
88
+ }
89
+ ```
90
+
91
+ > **Add `.localess/credentials.json` to your `.gitignore`** to avoid committing secrets.
92
+
93
+ ---
94
+
95
+ ## Translations
96
+
97
+ ### Push Translations
98
+
99
+ Upload a local JSON translation file to Localess.
100
+
101
+ ```bash
102
+ localess translations push <locale> --path <file> [options]
103
+ ```
104
+
105
+ **Arguments:**
106
+
107
+ | Argument | Description |
108
+ |------------|-------------------------------------|
109
+ | `<locale>` | ISO 639-1 locale code: `en`, `de`… |
110
+
111
+ **Options:**
112
+
113
+ | Flag | Default | Description |
114
+ |-------------------------|-----------------|-------------------------------------------------------|
115
+ | `-p, --path <path>` | required | Path to the translations JSON file |
116
+ | `-f, --format <format>` | `flat` | File format: `flat` only (**nested not yet implemented**) |
117
+ | `-t, --type <type>` | `add-missing` | Update strategy: `add-missing` or `update-existing` |
118
+ | `--dry-run` | `false` | Preview changes without applying them |
119
+
120
+ **Update Strategies:**
121
+
122
+ | Strategy | Behaviour |
123
+ |--------------------|------------------------------------------------------------|
124
+ | `add-missing` | Only adds keys that don't yet exist in Localess |
125
+ | `update-existing` | Only updates keys that already exist in Localess |
126
+
127
+ **File Formats:**
128
+
129
+ *Flat (default — only supported format for push):*
130
+ ```json
131
+ {
132
+ "common.submit": "Submit",
133
+ "nav.home": "Home",
134
+ "errors.required": "This field is required"
135
+ }
136
+ ```
137
+
138
+ > **⚠️ Nested format is not yet implemented for push.** Passing `--format nested` logs an error and exits without uploading. Use `flat` format only.
139
+
140
+ **Examples:**
141
+
142
+ ```bash
143
+ # Basic push — add missing translations only
144
+ localess translations push en --path ./locales/en.json
145
+
146
+ # Update existing translations (don't add new)
147
+ localess translations push de --path ./locales/de.json --type update-existing
148
+
149
+ # Preview changes without applying
150
+ localess translations push fr --path ./locales/fr.json --dry-run
151
+ ```
152
+
153
+ ---
154
+
155
+ ### Pull Translations
156
+
157
+ Download translations from Localess to a local JSON file.
158
+
159
+ ```bash
160
+ localess translations pull <locale> --path <file> [options]
161
+ ```
162
+
163
+ **Arguments:**
164
+
165
+ | Argument | Description |
166
+ |------------|-------------------------------------|
167
+ | `<locale>` | ISO 639-1 locale code: `en`, `de`… |
168
+
169
+ **Options:**
170
+
171
+ | Flag | Default | Description |
172
+ |-------------------------|-----------|--------------------------------------|
173
+ | `-p, --path <path>` | required | Output file path |
174
+ | `-f, --format <format>` | `flat` | File format: `flat` or `nested` |
175
+
176
+ **Examples:**
177
+
178
+ ```bash
179
+ # Pull as flat JSON
180
+ localess translations pull en --path ./locales/en.json
181
+
182
+ # Pull as nested JSON
183
+ localess translations pull de --path ./locales/de.json --format nested
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Type Generation
189
+
190
+ Generate TypeScript type definitions from your Localess space's OpenAPI schema.
191
+
192
+ ```bash
193
+ localess types generate [--path <output>]
194
+ ```
195
+
196
+ **Options:**
197
+
198
+ | Flag | Default | Description |
199
+ |---------------------|-------------------------------|-------------------------------|
200
+ | `-p, --path <path>` | `.localess/localess.d.ts` | Output file path |
201
+
202
+ > **Prerequisite:** The API token must have the **Development Tools** permission in Localess Space settings.
203
+
204
+ **What it does:**
205
+ 1. Fetches the OpenAPI 3.0 spec from your space
206
+ 2. Extracts schema components
207
+ 3. Generates TypeScript `.d.ts` definitions with `openapi-typescript`
208
+
209
+ **Examples:**
210
+
211
+ ```bash
212
+ # Default output to .localess/localess.d.ts
213
+ localess types generate
214
+
215
+ # Custom output path
216
+ localess types generate --path src/types/localess.d.ts
217
+ ```
218
+
219
+ **Generated output:**
220
+
221
+ ```typescript
222
+ // .localess/localess.d.ts (auto-generated — do not edit)
223
+ export type Page = {
224
+ _id: string;
225
+ _schema: string;
226
+ title: string;
227
+ body: (HeroSection | CardGrid | RichTextBlock)[];
228
+ };
229
+
230
+ export type HeroSection = {
231
+ _id: string;
232
+ _schema: string;
233
+ headline: string;
234
+ subheadline?: string;
235
+ image?: ContentAsset;
236
+ };
237
+
238
+ // ... all schemas from your Localess space
239
+ ```
240
+
241
+ **Using generated types:**
242
+
243
+ ```typescript
244
+ import type { Page, HeroSection } from './.localess/localess';
245
+ import { getLocalessClient } from "@localess/react";
246
+
247
+ const client = getLocalessClient();
248
+ const content = await client.getContentBySlug<Page>('home');
249
+ // content.data is fully typed as Page
250
+ ```
251
+
252
+ ---
253
+
254
+ ## CI/CD Integration
255
+
256
+ ### GitHub Actions Example
257
+
258
+ ```yaml
259
+ # .github/workflows/sync-translations.yml
260
+ name: Sync translations
261
+
262
+ on:
263
+ push:
264
+ paths:
265
+ - 'locales/**'
266
+
267
+ jobs:
268
+ push-translations:
269
+ runs-on: ubuntu-latest
270
+ steps:
271
+ - uses: actions/checkout@v4
272
+ - uses: actions/setup-node@v4
273
+ with:
274
+ node-version: '20'
275
+ - run: npm install -g @localess/cli
276
+ - run: localess translations push en --path ./locales/en.json
277
+ env:
278
+ LOCALESS_ORIGIN: ${{ secrets.LOCALESS_ORIGIN }}
279
+ LOCALESS_SPACE: ${{ secrets.LOCALESS_SPACE_ID }}
280
+ LOCALESS_TOKEN: ${{ secrets.LOCALESS_TOKEN }}
281
+ ```
282
+
283
+ ### Pull Translations in CI
284
+
285
+ ```yaml
286
+ - run: localess translations pull en --path ./locales/en.json
287
+ env:
288
+ LOCALESS_ORIGIN: ${{ secrets.LOCALESS_ORIGIN }}
289
+ LOCALESS_SPACE: ${{ secrets.LOCALESS_SPACE_ID }}
290
+ LOCALESS_TOKEN: ${{ secrets.LOCALESS_TOKEN }}
291
+ - run: git diff --exit-code locales/ || (git commit -am "chore: sync translations" && git push)
292
+ ```
293
+
294
+ ### Generate Types in CI
295
+
296
+ ```yaml
297
+ - run: localess types generate --path src/types/localess.d.ts
298
+ env:
299
+ LOCALESS_ORIGIN: ${{ secrets.LOCALESS_ORIGIN }}
300
+ LOCALESS_SPACE: ${{ secrets.LOCALESS_SPACE_ID }}
301
+ LOCALESS_TOKEN: ${{ secrets.LOCALESS_TOKEN }}
302
+ ```
303
+
304
+ ---
305
+
306
+ ## Local Development Workflow
307
+
308
+ ```bash
309
+ # 1. Authenticate once
310
+ localess login
311
+
312
+ # 2. Pull latest translations
313
+ localess translations pull en --path ./locales/en.json
314
+
315
+ # 3. Edit translations locally...
316
+
317
+ # 4. Push back (dry-run first)
318
+ localess translations push en --path ./locales/en.json --dry-run
319
+ localess translations push en --path ./locales/en.json
320
+
321
+ # 5. Generate types after schema changes in Localess
322
+ localess types generate
323
+ ```
324
+
325
+ ---
326
+
327
+ ## .gitignore Recommendations
328
+
329
+ ```gitignore
330
+ # Localess credentials (contains API token)
331
+ .localess/credentials.json
332
+
333
+ # Generated types are typically committed to the repo
334
+ # but can be excluded if auto-generated in CI:
335
+ # .localess/localess.d.ts
336
+ ```
337
+
338
+ ---
339
+
340
+ ## Files Written by the CLI
341
+
342
+ | File | Created by | Permissions | Purpose |
343
+ |-------------------------------|-----------------------------|----------------|--------------------------------|
344
+ | `.localess/credentials.json` | `localess login` | `0o600` (owner only) | Persisted auth credentials |
345
+ | `.localess/localess.d.ts` | `localess types generate` | Standard | Generated TypeScript types |
346
+
347
+ ---
348
+
349
+ ## Best Practices
350
+
351
+ 1. **Use environment variables in CI/CD** — set `LOCALESS_ORIGIN`, `LOCALESS_SPACE`, `LOCALESS_TOKEN` as secrets. No `login` command needed.
352
+
353
+ 2. **Always dry-run before pushing translations** in automated scripts: add `--dry-run` first, inspect the output, then run without it.
354
+
355
+ 3. **Commit generated types** (`.localess/localess.d.ts`) to your repo so the whole team benefits from type safety without running the CLI.
356
+
357
+ 4. **Re-run `types generate` after any schema change** in the Localess CMS to keep types in sync.
358
+
359
+ 5. **Use `add-missing` strategy (default)** for initial import of translations; switch to `update-existing` when syncing copy changes.
360
+
361
+ 6. **Never commit `.localess/credentials.json`** — add it to `.gitignore` immediately after `localess login`.
362
+
363
+ 7. **Give the token minimum required permissions** — the `types generate` command needs "Development Tools" permission; other commands only need standard API access.
package/dist/index.js CHANGED
@@ -123,6 +123,7 @@ function localessClient(options) {
123
123
  if (options.debug) {
124
124
  console.log(LOG_GROUP, "Client Options : ", options);
125
125
  }
126
+ const normalizedOrigin = options.origin.replace(/\/+$/, "");
126
127
  const fetchOptions = {
127
128
  redirect: "follow",
128
129
  headers: {
@@ -138,7 +139,7 @@ function localessClient(options) {
138
139
  if (options.debug) {
139
140
  console.log(LOG_GROUP, "getSpace()");
140
141
  }
141
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}?token=${options.token}`;
142
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}?token=${options.token}`;
142
143
  if (options.debug) {
143
144
  console.log(LOG_GROUP, "getSpace fetch url : ", url);
144
145
  }
@@ -177,7 +178,7 @@ function localessClient(options) {
177
178
  if (params?.excludeChildren) {
178
179
  excludeChildren = `&excludeChildren=${params.excludeChildren}`;
179
180
  }
180
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/links?token=${options.token}${kind}${parentSlug}${excludeChildren}`;
181
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/links?token=${options.token}${kind}${parentSlug}${excludeChildren}`;
181
182
  if (options.debug) {
182
183
  console.log(LOG_GROUP, "getLinks fetch url : ", url);
183
184
  }
@@ -215,7 +216,7 @@ function localessClient(options) {
215
216
  const locale = params?.locale ? `&locale=${params.locale}` : "";
216
217
  const resolveReference = params?.resolveReference ? `&resolveReference=${params.resolveReference}` : "";
217
218
  const resolveLink = params?.resolveLink ? `&resolveLink=${params.resolveLink}` : "";
218
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/contents/slugs/${slug}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
219
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/contents/slugs/${slug}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
219
220
  if (options.debug) {
220
221
  console.log(LOG_GROUP, "getContentBySlug fetch url : ", url);
221
222
  }
@@ -253,7 +254,7 @@ function localessClient(options) {
253
254
  const locale = params?.locale ? `&locale=${params.locale}` : "";
254
255
  const resolveReference = params?.resolveReference ? `&resolveReference=${params.resolveReference}` : "";
255
256
  const resolveLink = params?.resolveLink ? `&resolveLink=${params.resolveLink}` : "";
256
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/contents/${id}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
257
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/contents/${id}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
257
258
  if (options.debug) {
258
259
  console.log(LOG_GROUP, "getContentById fetch url : ", url);
259
260
  }
@@ -288,7 +289,7 @@ function localessClient(options) {
288
289
  if (params?.version && params.version == "draft") {
289
290
  version = `&version=${params.version}`;
290
291
  }
291
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/translations/${locale}?token=${options.token}${version}`;
292
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/translations/${locale}?token=${options.token}${version}`;
292
293
  if (options.debug) {
293
294
  console.log(LOG_GROUP, "getTranslations fetch url : ", url);
294
295
  }
@@ -317,7 +318,7 @@ function localessClient(options) {
317
318
  console.log(LOG_GROUP, "updateTranslations() type : ", type);
318
319
  console.log(LOG_GROUP, "updateTranslations() values : ", JSON.stringify(values));
319
320
  }
320
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/translations/${locale}`;
321
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/translations/${locale}`;
321
322
  if (options.debug) {
322
323
  console.log(LOG_GROUP, "updateTranslations fetch url : ", url);
323
324
  }
@@ -347,7 +348,7 @@ function localessClient(options) {
347
348
  if (options.debug) {
348
349
  console.log(LOG_GROUP, "getOpenApi()");
349
350
  }
350
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/open-api?token=${options.token}`;
351
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/open-api?token=${options.token}`;
351
352
  if (options.debug) {
352
353
  console.log(LOG_GROUP, "getOpenApi fetch url : ", url);
353
354
  }
@@ -371,13 +372,13 @@ function localessClient(options) {
371
372
  }
372
373
  },
373
374
  syncScriptUrl() {
374
- return `${options.origin}/scripts/sync-v1.js`;
375
+ return `${normalizedOrigin}/scripts/sync-v1.js`;
375
376
  },
376
377
  assetLink(asset) {
377
378
  if (typeof asset === "string") {
378
- return `${options.origin}/api/v1/spaces/${options.spaceId}/assets/${asset}`;
379
+ return `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/assets/${asset}`;
379
380
  } else {
380
- return `${options.origin}/api/v1/spaces/${options.spaceId}/assets/${asset.uri}`;
381
+ return `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/assets/${asset.uri}`;
381
382
  }
382
383
  }
383
384
  };
package/dist/index.mjs CHANGED
@@ -100,6 +100,7 @@ function localessClient(options) {
100
100
  if (options.debug) {
101
101
  console.log(LOG_GROUP, "Client Options : ", options);
102
102
  }
103
+ const normalizedOrigin = options.origin.replace(/\/+$/, "");
103
104
  const fetchOptions = {
104
105
  redirect: "follow",
105
106
  headers: {
@@ -115,7 +116,7 @@ function localessClient(options) {
115
116
  if (options.debug) {
116
117
  console.log(LOG_GROUP, "getSpace()");
117
118
  }
118
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}?token=${options.token}`;
119
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}?token=${options.token}`;
119
120
  if (options.debug) {
120
121
  console.log(LOG_GROUP, "getSpace fetch url : ", url);
121
122
  }
@@ -154,7 +155,7 @@ function localessClient(options) {
154
155
  if (params?.excludeChildren) {
155
156
  excludeChildren = `&excludeChildren=${params.excludeChildren}`;
156
157
  }
157
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/links?token=${options.token}${kind}${parentSlug}${excludeChildren}`;
158
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/links?token=${options.token}${kind}${parentSlug}${excludeChildren}`;
158
159
  if (options.debug) {
159
160
  console.log(LOG_GROUP, "getLinks fetch url : ", url);
160
161
  }
@@ -192,7 +193,7 @@ function localessClient(options) {
192
193
  const locale = params?.locale ? `&locale=${params.locale}` : "";
193
194
  const resolveReference = params?.resolveReference ? `&resolveReference=${params.resolveReference}` : "";
194
195
  const resolveLink = params?.resolveLink ? `&resolveLink=${params.resolveLink}` : "";
195
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/contents/slugs/${slug}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
196
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/contents/slugs/${slug}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
196
197
  if (options.debug) {
197
198
  console.log(LOG_GROUP, "getContentBySlug fetch url : ", url);
198
199
  }
@@ -230,7 +231,7 @@ function localessClient(options) {
230
231
  const locale = params?.locale ? `&locale=${params.locale}` : "";
231
232
  const resolveReference = params?.resolveReference ? `&resolveReference=${params.resolveReference}` : "";
232
233
  const resolveLink = params?.resolveLink ? `&resolveLink=${params.resolveLink}` : "";
233
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/contents/${id}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
234
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/contents/${id}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
234
235
  if (options.debug) {
235
236
  console.log(LOG_GROUP, "getContentById fetch url : ", url);
236
237
  }
@@ -265,7 +266,7 @@ function localessClient(options) {
265
266
  if (params?.version && params.version == "draft") {
266
267
  version = `&version=${params.version}`;
267
268
  }
268
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/translations/${locale}?token=${options.token}${version}`;
269
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/translations/${locale}?token=${options.token}${version}`;
269
270
  if (options.debug) {
270
271
  console.log(LOG_GROUP, "getTranslations fetch url : ", url);
271
272
  }
@@ -294,7 +295,7 @@ function localessClient(options) {
294
295
  console.log(LOG_GROUP, "updateTranslations() type : ", type);
295
296
  console.log(LOG_GROUP, "updateTranslations() values : ", JSON.stringify(values));
296
297
  }
297
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/translations/${locale}`;
298
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/translations/${locale}`;
298
299
  if (options.debug) {
299
300
  console.log(LOG_GROUP, "updateTranslations fetch url : ", url);
300
301
  }
@@ -324,7 +325,7 @@ function localessClient(options) {
324
325
  if (options.debug) {
325
326
  console.log(LOG_GROUP, "getOpenApi()");
326
327
  }
327
- let url = `${options.origin}/api/v1/spaces/${options.spaceId}/open-api?token=${options.token}`;
328
+ let url = `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/open-api?token=${options.token}`;
328
329
  if (options.debug) {
329
330
  console.log(LOG_GROUP, "getOpenApi fetch url : ", url);
330
331
  }
@@ -348,13 +349,13 @@ function localessClient(options) {
348
349
  }
349
350
  },
350
351
  syncScriptUrl() {
351
- return `${options.origin}/scripts/sync-v1.js`;
352
+ return `${normalizedOrigin}/scripts/sync-v1.js`;
352
353
  },
353
354
  assetLink(asset) {
354
355
  if (typeof asset === "string") {
355
- return `${options.origin}/api/v1/spaces/${options.spaceId}/assets/${asset}`;
356
+ return `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/assets/${asset}`;
356
357
  } else {
357
- return `${options.origin}/api/v1/spaces/${options.spaceId}/assets/${asset.uri}`;
358
+ return `${normalizedOrigin}/api/v1/spaces/${options.spaceId}/assets/${asset.uri}`;
358
359
  }
359
360
  }
360
361
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@localess/cli",
3
- "version": "3.0.0",
3
+ "version": "3.0.1-dev.20260325211243",
4
4
  "description": "Localess Command Line.",
5
5
  "keywords": [
6
6
  "localess",
@@ -13,7 +13,8 @@
13
13
  "homepage": "https://github.com/Lessify/localess-js",
14
14
  "sideEffects": false,
15
15
  "files": [
16
- "dist"
16
+ "dist",
17
+ "SKILL.md"
17
18
  ],
18
19
  "exports": {
19
20
  ".": {