octwin-cli 0.8.0 → 0.8.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/CHANGELOG.md +21 -0
- package/dist/index.js +47 -39
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ Format: [Keep a Changelog](https://keepachangelog.com/) — newest first, bucket
|
|
|
5
5
|
**Added · Changed · Deprecated · Removed · Fixed · Security**. The platform-wide view lives in the
|
|
6
6
|
repo root [`CHANGELOG.md`](../../CHANGELOG.md); this file is the CLI-only cut that ships with the package.
|
|
7
7
|
|
|
8
|
+
## [0.8.1] - 2026-08-27
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Logging in made `platform-kb pull` WORSE.** The command preferred the tenant-scoped route
|
|
12
|
+
whenever a token was saved, and that route is guarded by `pack:deploy` — so a developer holding a
|
|
13
|
+
token without that scope got a **403 on a pull that succeeds with no token at all**. It is the
|
|
14
|
+
likeliest token a new developer holds: no console preset could even reach `pack:deploy` before
|
|
15
|
+
2026-08-26. `platform-kb` now always uses the anonymous route and never sends a credential.
|
|
16
|
+
`--token` is accepted and ignored so existing scripts keep working.
|
|
17
|
+
|
|
18
|
+
The platform serves the *same* bundle from all three of its views — its route file states the
|
|
19
|
+
payload is identical and tenant-independent — so the authed route returned nothing extra and the
|
|
20
|
+
credential could only ever subtract. The two reasons the old code gave did not survive checking:
|
|
21
|
+
"keeps the author's own instance the source of truth" is vacuous (both routes are the same
|
|
22
|
+
instance), and "works against platforms that predate the public rung" is a backward-compatibility
|
|
23
|
+
shim this codebase does not carry.
|
|
24
|
+
- **The drift nudge now works without a token.** It polled through the same authed route, so an
|
|
25
|
+
author inspecting data with a narrow token was told, every time, that it *could not* check —
|
|
26
|
+
advice in place of the signal. Both the poll and the apology are gone: the poll is anonymous, so
|
|
27
|
+
it simply works.
|
|
28
|
+
|
|
8
29
|
## [0.8.0] - 2026-08-27
|
|
9
30
|
|
|
10
31
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -587,11 +587,12 @@ function diffKbIndex(prev, next) {
|
|
|
587
587
|
}
|
|
588
588
|
/**
|
|
589
589
|
* Fetch the platform's KB identity (`?meta=1`) — the cheap poll behind both the
|
|
590
|
-
* drift nudge and `--if-stale`. Returns
|
|
590
|
+
* drift nudge and `--if-stale`. Returns not-ok on anything that is not a clean
|
|
591
591
|
* answer; the caller decides whether that is worth a word.
|
|
592
592
|
*
|
|
593
|
-
* `notAuthorized`
|
|
594
|
-
*
|
|
593
|
+
* There is no `notAuthorized` case any more. `kbEndpoint` is anonymous, so this
|
|
594
|
+
* poll cannot be refused for lack of a scope — which used to be its most common
|
|
595
|
+
* failure, and the reason the drift nudge below carried a whole branch of advice.
|
|
595
596
|
*/
|
|
596
597
|
async function fetchKbMeta(t, timeoutMs = 2_000) {
|
|
597
598
|
const ep = kbEndpoint(t);
|
|
@@ -602,12 +603,12 @@ async function fetchKbMeta(t, timeoutMs = 2_000) {
|
|
|
602
603
|
// socket checked out of the pool, and this poll runs on the way to a possible `exitNow` —
|
|
603
604
|
// a held socket at exit is a pending libuv handle.
|
|
604
605
|
await res.arrayBuffer().catch(() => undefined);
|
|
605
|
-
return { ok: false
|
|
606
|
+
return { ok: false };
|
|
606
607
|
}
|
|
607
608
|
return { ok: true, meta: await res.json() };
|
|
608
609
|
}
|
|
609
610
|
catch {
|
|
610
|
-
return { ok: false
|
|
611
|
+
return { ok: false };
|
|
611
612
|
}
|
|
612
613
|
}
|
|
613
614
|
/** Nudge (to stderr) when the platform's capability KB has changed since the last
|
|
@@ -628,24 +629,20 @@ async function kbStaleNotice(flags) {
|
|
|
628
629
|
const local = readLocalKb(packDir);
|
|
629
630
|
if (!local?.content_hash)
|
|
630
631
|
return []; // never pulled → the skill already says to pull
|
|
631
|
-
|
|
632
|
-
|
|
632
|
+
// URL only, deliberately: the KB poll is anonymous, so requiring a token here would
|
|
633
|
+
// silence the nudge for exactly the authors who most need it. This used to call
|
|
634
|
+
// `resolveTargetOrNull` (url AND token) against the tenant-scoped route, which needs
|
|
635
|
+
// `pack:deploy` — so an author inspecting data with a narrow (`records:read`-only)
|
|
636
|
+
// token got NO drift signal at all, silently, and a stale reference is precisely what
|
|
637
|
+
// makes an author invent a primitive from memory. The branch that apologised for that
|
|
638
|
+
// is gone with the cause; every remaining failure (offline, timeout, a platform
|
|
639
|
+
// serving no reference) stays silent, because observing must not break a command.
|
|
640
|
+
const t = readTarget(flags);
|
|
641
|
+
if (!t.url)
|
|
633
642
|
return [];
|
|
634
643
|
const polled = await fetchKbMeta(t);
|
|
635
|
-
if (!polled.ok)
|
|
636
|
-
// The tenant-scoped meta poll needs `pack:deploy`, but this nudge rides on every
|
|
637
|
-
// networked command — so an author inspecting data with a narrow (`records:read`-only)
|
|
638
|
-
// token got NO drift signal at all, silently, and a stale reference is exactly what
|
|
639
|
-
// makes an author invent a primitive from memory. Say so once; stay silent for every
|
|
640
|
-
// other failure (offline, timeout, a platform without the route).
|
|
641
|
-
if (polled.notAuthorized) {
|
|
642
|
-
return [
|
|
643
|
-
'\nⓘ can\'t check whether the platform capability reference drifted — that token lacks `pack:deploy`.',
|
|
644
|
-
' Check it without a token: octwin platform-kb --check (or refresh: octwin platform-kb --token oct_…)',
|
|
645
|
-
];
|
|
646
|
-
}
|
|
644
|
+
if (!polled.ok)
|
|
647
645
|
return [];
|
|
648
|
-
}
|
|
649
646
|
const meta = polled.meta;
|
|
650
647
|
if (meta.content_hash && meta.content_hash !== local.content_hash) {
|
|
651
648
|
// Per-entry summary (now that the index carries per-entry hashes) — the
|
|
@@ -1207,20 +1204,32 @@ function resolveTargetOrNull(flags) {
|
|
|
1207
1204
|
/**
|
|
1208
1205
|
* Where to read the platform capability reference from, and how.
|
|
1209
1206
|
*
|
|
1210
|
-
* The KB is tenant-independent platform stdlib, and
|
|
1211
|
-
*
|
|
1212
|
-
*
|
|
1213
|
-
*
|
|
1214
|
-
*
|
|
1207
|
+
* ALWAYS the anonymous route. The KB is tenant-independent platform stdlib, and
|
|
1208
|
+
* the platform serves the SAME bundle from all three of its views -- its own
|
|
1209
|
+
* route file says so twice ("THREE views of the SAME `getPlatformKbBundle()`";
|
|
1210
|
+
* "the payload is identical") and forbids tenant data ever entering the reader.
|
|
1211
|
+
* So the authed route returns nothing extra, and asking for a credential to read
|
|
1212
|
+
* it can only ever subtract.
|
|
1213
|
+
*
|
|
1214
|
+
* It subtracted, measurably. This used to prefer the tenant-scoped route
|
|
1215
|
+
* WHENEVER a token was saved, and that route is guarded by `pack:deploy` -- so a
|
|
1216
|
+
* developer holding a token WITHOUT that scope got a 403 on a pull that would
|
|
1217
|
+
* have succeeded with no token at all. Having logged in made the CLI strictly
|
|
1218
|
+
* worse than not having logged in, on the very first command an author runs. No
|
|
1219
|
+
* console preset could even reach `pack:deploy` before 2026-08-26, so that was
|
|
1220
|
+
* the likeliest token a new developer held.
|
|
1221
|
+
*
|
|
1222
|
+
* The two arguments the old comment gave for preferring a token did not survive
|
|
1223
|
+
* being checked: "keeps the author's own instance the source of truth" is
|
|
1224
|
+
* vacuous (both routes are `t.url` -- the same instance), and "works against
|
|
1225
|
+
* platforms that predate the public rung" is a backward-compatibility shim,
|
|
1226
|
+
* which this codebase does not carry.
|
|
1215
1227
|
*
|
|
1216
|
-
*
|
|
1217
|
-
*
|
|
1218
|
-
* rung, and using it keeps the author's own instance the source of truth.
|
|
1228
|
+
* `--token` is still ACCEPTED here, and ignored, so a scripted `platform-kb pull
|
|
1229
|
+
* --token …` keeps working instead of turning into an unknown-flag error.
|
|
1219
1230
|
*/
|
|
1220
1231
|
function kbEndpoint(t) {
|
|
1221
|
-
return t.
|
|
1222
|
-
? { url: `${t.url}/api/self/t/octwin-platform-kb`, headers: authHeaders(t), anonymous: false }
|
|
1223
|
-
: { url: `${t.url}/api/public/octwin-platform-kb`, headers: {}, anonymous: true };
|
|
1232
|
+
return { url: `${t.url}/api/public/octwin-platform-kb`, headers: {} };
|
|
1224
1233
|
}
|
|
1225
1234
|
/** The raw resolution both wrappers share — may return empty url/token. */
|
|
1226
1235
|
function readTarget(flags) {
|
|
@@ -1756,9 +1765,7 @@ async function cmdPlatformKb(flags) {
|
|
|
1756
1765
|
const local = readLocalKb(packDir);
|
|
1757
1766
|
const polled = await fetchKbMeta(t, 10_000);
|
|
1758
1767
|
if (!polled.ok) {
|
|
1759
|
-
console.error(
|
|
1760
|
-
? '✗ cannot check — the platform refused the token, and this instance serves no anonymous reference.'
|
|
1761
|
-
: `✗ cannot check — ${url} did not answer.`);
|
|
1768
|
+
console.error(`✗ cannot check — ${url} did not answer, or serves no capability reference.`);
|
|
1762
1769
|
exitNow(1);
|
|
1763
1770
|
}
|
|
1764
1771
|
const remote = polled.meta.content_hash;
|
|
@@ -1787,7 +1794,7 @@ async function cmdPlatformKb(flags) {
|
|
|
1787
1794
|
return;
|
|
1788
1795
|
}
|
|
1789
1796
|
}
|
|
1790
|
-
console.log(`→ Pulling the platform capability reference from ${url}
|
|
1797
|
+
console.log(`→ Pulling the platform capability reference from ${url} (no token needed for the reference) …`);
|
|
1791
1798
|
const res = await fetchOrDie(ep.url, { headers: ep.headers }, 'platform-kb pull');
|
|
1792
1799
|
const text = await res.text();
|
|
1793
1800
|
if (!res.ok) {
|
|
@@ -5162,14 +5169,15 @@ octwin projects rm <slug> [--yes]
|
|
|
5162
5169
|
INDEX.md (the corpus) · SYMBOLS.md (every name -> its file; grep this) ·
|
|
5163
5170
|
OUTLINE.md (every heading with its line number).
|
|
5164
5171
|
|
|
5165
|
-
NO TOKEN NEEDED — the reference is platform stdlib and is served anonymously
|
|
5166
|
-
|
|
5172
|
+
NO TOKEN NEEDED — the reference is platform stdlib and is served anonymously,
|
|
5173
|
+
and this command never sends one. --token is accepted and ignored, so an older
|
|
5174
|
+
script that passes it keeps working.
|
|
5167
5175
|
|
|
5168
5176
|
--if-stale poll the platform's content_hash first and skip the download when
|
|
5169
5177
|
nothing changed. Cheap enough to run at the start of every session.
|
|
5170
5178
|
--check report only, write nothing. Exit 0 = current, 2 = stale or never
|
|
5171
|
-
pulled, 1 = could not tell (offline /
|
|
5172
|
-
agent loops that want to branch without parsing prose.`,
|
|
5179
|
+
pulled, 1 = could not tell (offline / no reference served). For
|
|
5180
|
+
scripts and agent loops that want to branch without parsing prose.`,
|
|
5173
5181
|
test: `octwin test [--dir .]
|
|
5174
5182
|
Alias for \`octwin validate --remote\` — the full platform check.`,
|
|
5175
5183
|
memos: `octwin memos [--all] [--json]
|
package/package.json
CHANGED