altalandi 0.5.0 → 0.6.0
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 +79 -0
- package/altalandi.mjs +54 -3
- package/batch.mjs +620 -0
- package/client.d.ts +122 -117
- package/client.mjs +219 -209
- package/package.json +5 -2
- package/report.mjs +401 -280
- package/segment.mjs +1059 -0
- package/skill/SKILL.md +20 -1
- package/update.mjs +3 -1
- package/web.mjs +558 -0
package/README.md
CHANGED
|
@@ -114,6 +114,85 @@ altalandi --report --file draft.txt # also write a Markdown report to ./altal
|
|
|
114
114
|
| `--report-dir <dir>` | | write the report to `<dir>` instead (implies `--report`) |
|
|
115
115
|
| `--fast` | off | `preserveMeaning:false`, `scoreNativeness:false`, `onlyImprove:false`, `polishPass:false`, `scoring:"none"` — normally one model call (structured input can still add a structure-preserving retry) |
|
|
116
116
|
|
|
117
|
+
## Folders, i18n catalogs and websites
|
|
118
|
+
|
|
119
|
+
One command for a whole folder, one web page, or a site. The input is never
|
|
120
|
+
written to.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
altalandi ./docs # every .md .txt .html .json under ./docs → ./docs-is
|
|
124
|
+
altalandi ./locales --out ./locales-is # i18n catalogs: values only, placeholders kept
|
|
125
|
+
altalandi ./docs --dry-run # every passage and the estimate; sends nothing
|
|
126
|
+
altalandi https://example.com/pricing --out pricing-is.md # one page → Markdown
|
|
127
|
+
altalandi https://example.com/docs/ --site --max-pages 50 # a site → ./example.com-is/
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Each passage (a paragraph, list item, table cell, heading or string) is its own
|
|
131
|
+
call. Everything outside the passages is written back byte for byte:
|
|
132
|
+
|
|
133
|
+
| format | refined | never touched |
|
|
134
|
+
|--------|---------|---------------|
|
|
135
|
+
| Markdown | paragraphs, list items, table cells, heading text, front-matter `title`/`description`-style values | code blocks, inline code, URLs, link targets, HTML tags, front-matter keys, heading markup |
|
|
136
|
+
| HTML | text in elements (inline tags travel as numbered markers and come back exactly) | tags, attributes, entities, `script`, `style`, `pre`, `code`, `translate="no"` |
|
|
137
|
+
| JSON | string values of i18n catalogs | keys, `@`/`$` metadata, `{name}` `{{name}}` `%s` `%(name)s`, ICU plural/select structure, HTML in strings |
|
|
138
|
+
| Text | paragraphs (re-wrapped to their width) and lines | |
|
|
139
|
+
|
|
140
|
+
Passages with nothing to refine (code, numbers, URLs or placeholders alone) are
|
|
141
|
+
skipped, and a passage that appears twice is sent once. `node_modules`, `.git`,
|
|
142
|
+
`dist`, `build`, `altalandi-reports`, hidden files and folders and symlinks are
|
|
143
|
+
skipped. JSON config (`package.json`, `tsconfig.json`, …) and JSON with numbers
|
|
144
|
+
or booleans is copied unchanged; other file types are not copied.
|
|
145
|
+
|
|
146
|
+
Pages are fetched one at a time with a User-Agent naming altalandi, only where
|
|
147
|
+
robots.txt allows it for `altalandi` and for `*` (`Crawl-delay` is honoured).
|
|
148
|
+
The readable part (`<main>` or `<article>`, else `<body>` without navigation,
|
|
149
|
+
header, footer, sidebars, forms, scripts and hidden elements) becomes Markdown.
|
|
150
|
+
`--site` reads `/sitemap.xml`, or follows links when there is none, staying on
|
|
151
|
+
the same origin and under the URL's folder.
|
|
152
|
+
|
|
153
|
+
**Cost and safety.** Before anything is sent, the CLI prints an estimate, priced
|
|
154
|
+
from the public tariff:
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
5 files, 24 passages, about 851 characters (23 calls: repeated passages are sent once).
|
|
158
|
+
1 more file has nothing to refine and is copied unchanged.
|
|
159
|
+
Billing: about 920 units (at least 40 a call), about 74 kr if paid from credit. The free allowance is 2,000 units a day.
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Every call costs at least the per-call minimum, so many short strings cost more
|
|
163
|
+
than their word count. In a terminal it asks before sending; without one (CI,
|
|
164
|
+
agents) it sends nothing unless you pass `--yes`.
|
|
165
|
+
|
|
166
|
+
| flag | default | meaning |
|
|
167
|
+
|------|---------|---------|
|
|
168
|
+
| `--out <path>` | `<folder>-is` | output folder, refused if it is the input, inside it or around it; for one page, the `.md` file |
|
|
169
|
+
| `--dry-run` | off | list every passage and the estimate; send nothing, write nothing |
|
|
170
|
+
| `--yes`, `-y` | off | send without asking |
|
|
171
|
+
| `--max-chars <n>` | | files are sent in order until the next would go over n characters; the rest are neither sent nor written |
|
|
172
|
+
| `--concurrency <n>` | `2` | calls at once, 1–4 |
|
|
173
|
+
| `--site` / `--max-pages <n>` | off / `20` | read a site from the URL, up to n pages |
|
|
174
|
+
| `--no-store` | off | keep none of the passages (free use is otherwise kept, scrubbed of personal data) |
|
|
175
|
+
| `--json` | off | a machine-readable summary of every file and passage on stdout |
|
|
176
|
+
|
|
177
|
+
A `429` waits as long as `Retry-After` asks (up to 5 minutes) and retries. A
|
|
178
|
+
`402` stops the run cleanly: finished files are written, the rest are listed and
|
|
179
|
+
not written; run the same command again after topping up.
|
|
180
|
+
|
|
181
|
+
**Results.** Only verified changes are written. A passage keeps its original
|
|
182
|
+
text when its meaning check did not pass (`meaningCheck` other than `passed`, or
|
|
183
|
+
`meaningPreserved` not `true`), or when the refined text lost or changed code, a
|
|
184
|
+
URL, a placeholder or a tag. One report next to the output
|
|
185
|
+
(`altalandi-reports/altalandi-<time>-<input>.md`) lists every file and every
|
|
186
|
+
changed passage with its meaning verdict, and the kept passages under their own
|
|
187
|
+
heading with the unused suggestion and the reason.
|
|
188
|
+
|
|
189
|
+
| exit | meaning |
|
|
190
|
+
|------|---------|
|
|
191
|
+
| `0` | done |
|
|
192
|
+
| `2` | done, but some passages kept their original text (see the report) |
|
|
193
|
+
| `4` | stopped: out of credit or allowance |
|
|
194
|
+
| `1` | error, or not everything was done (a failed call, `--max-chars`, no `--yes`) |
|
|
195
|
+
|
|
117
196
|
## Before and after
|
|
118
197
|
|
|
119
198
|
`--diff` prints what the website shows inline, in the terminal:
|
package/altalandi.mjs
CHANGED
|
@@ -5,10 +5,14 @@
|
|
|
5
5
|
// npx altalandi login # once: connects this computer to your account
|
|
6
6
|
// altalandi "Make sure to reach out to us today."
|
|
7
7
|
// echo "some text" | altalandi --domain marketing
|
|
8
|
+
// altalandi ./docs # a whole folder → ./docs-is (batch.mjs)
|
|
9
|
+
// altalandi https://example.com --site
|
|
8
10
|
//
|
|
9
11
|
// stdout = the refined text (so you can pipe it); stderr = nativeness score,
|
|
10
12
|
// meaning verdict, API warnings, and the diff.
|
|
11
13
|
|
|
14
|
+
import { existsSync, statSync } from "node:fs";
|
|
15
|
+
|
|
12
16
|
import { refine } from "./client.mjs";
|
|
13
17
|
import { beforeAfterText, costLines, outcomeOf, reportEnabled, reviewMarkdown, writeReport } from "./report.mjs";
|
|
14
18
|
import { readCredentials, resolveKey, runLogin, runLogout, runWhoami } from "./auth.mjs";
|
|
@@ -72,7 +76,10 @@ applied · 1 the check or the update failed.`);
|
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
if (!handled) {
|
|
75
|
-
const opts = {
|
|
79
|
+
const opts = {
|
|
80
|
+
domain: "general", register: "neutral", json: false, fast: false, diff: false, review: false, report: undefined,
|
|
81
|
+
site: false, dryRun: false, yes: false,
|
|
82
|
+
};
|
|
76
83
|
const positional = [];
|
|
77
84
|
for (let i = 0; i < args.length; i++) {
|
|
78
85
|
const a = args[i];
|
|
@@ -90,6 +97,12 @@ for (let i = 0; i < args.length; i++) {
|
|
|
90
97
|
else if (a === "--no-store") opts.noStore = true;
|
|
91
98
|
else if (a === "--report") opts.report = true;
|
|
92
99
|
else if (a === "--report-dir") { opts.report = true; opts.reportDir = args[++i]; }
|
|
100
|
+
else if (a === "--site") opts.site = true;
|
|
101
|
+
else if (a === "--max-pages") opts.maxPages = Number(args[++i]);
|
|
102
|
+
else if (a === "--dry-run") opts.dryRun = true;
|
|
103
|
+
else if (a === "--yes" || a === "-y") opts.yes = true;
|
|
104
|
+
else if (a === "--max-chars") opts.maxChars = Number(args[++i]);
|
|
105
|
+
else if (a === "--concurrency") opts.concurrency = Number(args[++i]);
|
|
93
106
|
else positional.push(a);
|
|
94
107
|
}
|
|
95
108
|
|
|
@@ -105,6 +118,13 @@ Usage:
|
|
|
105
118
|
echo "text" | altalandi
|
|
106
119
|
altalandi --domain marketing --register warm "text"
|
|
107
120
|
|
|
121
|
+
Folders and websites:
|
|
122
|
+
altalandi ./docs every .md .txt .html .json under ./docs → ./docs-is
|
|
123
|
+
altalandi ./locales --out ./locales-is
|
|
124
|
+
altalandi https://example.com/about --out about-is.md one page, as Markdown
|
|
125
|
+
altalandi https://example.com --site a site: /sitemap.xml, else its links
|
|
126
|
+
altalandi ./docs --dry-run list what would be sent and the estimate; send nothing
|
|
127
|
+
|
|
108
128
|
Commands:
|
|
109
129
|
altalandi login open altalandi.is, sign in or sign up, approve; stores the key
|
|
110
130
|
and installs the Claude Code skill (--no-skill, --no-browser)
|
|
@@ -122,7 +142,8 @@ Options:
|
|
|
122
142
|
--diff show the text before and after, and every change with its reason (stderr)
|
|
123
143
|
--review print one Markdown block on stdout to show the user as it is: verdict,
|
|
124
144
|
before, after, every change with its reason, the cost and the balance
|
|
125
|
-
--out write the refined text to this UTF-8 file (use with --review
|
|
145
|
+
--out write the refined text to this UTF-8 file (use with --review;
|
|
146
|
+
for a folder or a URL, see below)
|
|
126
147
|
--no-store don't keep this text: free use is otherwise kept, scrubbed of personal
|
|
127
148
|
data, to improve the service (paid use is kept only with consent)
|
|
128
149
|
--report also write a Markdown report (input, output, changes, reasons, verdict)
|
|
@@ -131,7 +152,23 @@ Options:
|
|
|
131
152
|
--base-url API base (default $ALTALANDI_BASE_URL or https://altalandi.is/api)
|
|
132
153
|
|
|
133
154
|
stdout = refined text; stderr = nativeness / meaning / warnings / diff.
|
|
134
|
-
Without --fast, false/null meaning verification exits 3 after printing the response
|
|
155
|
+
Without --fast, false/null meaning verification exits 3 after printing the response.
|
|
156
|
+
|
|
157
|
+
Folder and website options:
|
|
158
|
+
--out output folder (default <folder>-is; never the input or inside it),
|
|
159
|
+
or the .md file for one page
|
|
160
|
+
--dry-run show every passage and the estimate; send nothing, write nothing
|
|
161
|
+
--yes, -y send without asking (needed when there is no terminal to ask in)
|
|
162
|
+
--max-chars send at most this many characters; files past the budget are left out
|
|
163
|
+
--concurrency calls at once, 1-4 (default 2)
|
|
164
|
+
--site with a URL: its pages on the same site, under the URL's folder
|
|
165
|
+
--max-pages pages to read with --site (default 20)
|
|
166
|
+
--json print a machine-readable summary at the end
|
|
167
|
+
Code, URLs, link targets, placeholders ({name}, %s, ICU), HTML tags and JSON keys
|
|
168
|
+
stay byte for byte. Only changes whose meaning check passed are written; other
|
|
169
|
+
passages keep the original text and are listed in the report
|
|
170
|
+
(./altalandi-reports/). Exit: 0 done · 2 some passages kept their original ·
|
|
171
|
+
4 out of credit · 1 error.`);
|
|
135
172
|
}
|
|
136
173
|
|
|
137
174
|
function readStdin() {
|
|
@@ -152,6 +189,19 @@ if (opts.key) {
|
|
|
152
189
|
console.error("⚠ --key is deprecated because command arguments can leak through shell history/process listings; load ALTALANDI_API_KEY instead.");
|
|
153
190
|
}
|
|
154
191
|
|
|
192
|
+
// A folder or a URL as the one argument: many documents in one run (batch.mjs).
|
|
193
|
+
const target = positional.length === 1 && !opts.file ? positional[0] : "";
|
|
194
|
+
const many = /^https?:\/\//i.test(target) || (Boolean(target) && existsSync(target) && statSync(target).isDirectory());
|
|
195
|
+
if (many) {
|
|
196
|
+
const { runBatch } = await import("./batch.mjs");
|
|
197
|
+
const { currentVersion } = await import("./update.mjs");
|
|
198
|
+
process.exitCode = await runBatch({ ...opts, target, key, baseUrl }, { version: await currentVersion() });
|
|
199
|
+
} else {
|
|
200
|
+
if (opts.site || opts.dryRun || opts.maxChars !== undefined || opts.maxPages !== undefined || opts.concurrency !== undefined) {
|
|
201
|
+
console.error("--site, --dry-run, --max-chars, --max-pages and --concurrency need a folder or a URL: altalandi ./docs --dry-run");
|
|
202
|
+
process.exit(2);
|
|
203
|
+
}
|
|
204
|
+
|
|
155
205
|
let text = positional.join(" ").trim();
|
|
156
206
|
if (opts.file) {
|
|
157
207
|
const { readFile } = await import("node:fs/promises");
|
|
@@ -271,4 +321,5 @@ try {
|
|
|
271
321
|
if (e.status === 401) console.error("→ The key is invalid or revoked. Run: npx altalandi login");
|
|
272
322
|
process.exit(e.status === 402 ? 4 : 1);
|
|
273
323
|
}
|
|
324
|
+
} // single text
|
|
274
325
|
}
|