@nuwel-dev/setup 1.0.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/LICENSE +21 -0
- package/README.md +42 -0
- package/bin/setup.js +183 -0
- package/lib/wizard.js +97 -0
- package/package.json +36 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 nuwel (https://nuwel.jp)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @nuwel-dev/setup
|
|
2
|
+
|
|
3
|
+
figma-kit の**セットアップウィザード**(公開 npm・秘密ゼロ・依存ゼロ)。
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npx @nuwel-dev/setup
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
> **2 回目以降の実行**(ウィザードが `~/.npmrc` に `@nuwel-dev:registry=https://npm.pkg.github.com` を書いた後)は、
|
|
10
|
+
> この橋自身は公開 npm 側に在るため scope registry を明示して実行する:
|
|
11
|
+
> `npx --@nuwel-dev:registry=https://registry.npmjs.org @nuwel-dev/setup`
|
|
12
|
+
|
|
13
|
+
## これは何をするか
|
|
14
|
+
|
|
15
|
+
1. GitHub のトークン作成ページ URL を表示する(`read:packages` にチェック済みの URL)
|
|
16
|
+
2. 貼り付けられたトークンを受け取る(**画面には表示しない**)
|
|
17
|
+
3. `~/.npmrc` にスコープ行と authToken を書く(権限 **600**・他の行は壊さない)
|
|
18
|
+
4. 接続確認して、401 / 403 / 404 を**平語**で説明する(「招待をまだ Accept していない」等)
|
|
19
|
+
5. `npx @nuwel-dev/figma-kit init` へ委譲する
|
|
20
|
+
|
|
21
|
+
## なぜ公開 npm にあるのか
|
|
22
|
+
|
|
23
|
+
キット本体 `@nuwel-dev/figma-kit` は GitHub Packages の限定配布で、**トークンを設定しないと落とせない**。
|
|
24
|
+
その手順自体を限定配布側に置くと鶏と卵になる。この橋だけを公開側に置いて解く。
|
|
25
|
+
|
|
26
|
+
橋は攻撃面でもあるので、**秘密を 1 バイトも含まず・依存を 1 つも持たない**。
|
|
27
|
+
publish は provenance 付き(公開レジストリでのみ効く)。
|
|
28
|
+
|
|
29
|
+
## トークンの扱い(設計)
|
|
30
|
+
|
|
31
|
+
- 書き込み先は `~/.npmrc` だけ。案件ディレクトリの `.npmrc` には**スコープ行しか書かない**(`figma-kit init` が書く・`.gitignore` 済み)
|
|
32
|
+
- stdout・ログ・エラーメッセージにトークンを出す経路を持たない(表示は `redact()` のマスクのみ)
|
|
33
|
+
- 例外は種別(`e.code`)だけを表示する(例外メッセージにトークンが載る事故を構造で防ぐ)
|
|
34
|
+
|
|
35
|
+
## 非対話フラグ(テスト・CI 用)
|
|
36
|
+
|
|
37
|
+
| flag | 意味 |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `--no-verify` | 接続確認を行わない |
|
|
40
|
+
| `--no-init` | `figma-kit init` へ委譲しない |
|
|
41
|
+
| `--home=<dir>` | `~/.npmrc` の代わりに `<dir>/.npmrc` を書く |
|
|
42
|
+
| `--dir=<dir>` | init を実行する案件ディレクトリ(既定は cwd) |
|
package/bin/setup.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* @nuwel-dev/setup — 買い手の初日を 1 行で始めるための公開ウィザード(2026-08-23・ADR R2 §3.4 W2)。
|
|
5
|
+
*
|
|
6
|
+
* npx @nuwel-dev/setup
|
|
7
|
+
*
|
|
8
|
+
* なぜ公開 npm に置くのか: キット本体 `@nuwel-dev/figma-kit` は GitHub Packages(private)にあり、
|
|
9
|
+
* **トークンを設定しないと落とせない**。その設定手順自体を private 側に置くと鶏と卵になる。
|
|
10
|
+
* ここは秘密を 1 バイトも含まず(依存もゼロ)、やることは 4 つだけ:
|
|
11
|
+
* ① トークン作成ページの URL を出す → ② 貼ってもらう → ③ ~/.npmrc へ 0600 で書く →
|
|
12
|
+
* ④ 接続確認(401/404 を平語で説明)→ ⑤ `npx @nuwel-dev/figma-kit init` へ委譲
|
|
13
|
+
*
|
|
14
|
+
* **トークンは stdout・ログ・エラー文へ 1 度も出さない**(表示は lib/wizard.js の redact のみ)。
|
|
15
|
+
*
|
|
16
|
+
* flags(テスト・非対話用):
|
|
17
|
+
* --no-verify 接続確認を行わない(ネットワークが無い環境)
|
|
18
|
+
* --no-init figma-kit init へ委譲しない
|
|
19
|
+
* --home=<dir> ~/.npmrc の代わりに <dir>/.npmrc を書く(テスト)
|
|
20
|
+
* --dir=<dir> init を実行する案件ディレクトリ(既定は cwd)
|
|
21
|
+
*/
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const os = require('os');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const readline = require('readline');
|
|
26
|
+
const { spawnSync } = require('child_process');
|
|
27
|
+
const W = require('../lib/wizard');
|
|
28
|
+
|
|
29
|
+
const argv = process.argv.slice(2);
|
|
30
|
+
const flag = (k) => argv.includes(k);
|
|
31
|
+
const val = (k) => { const a = argv.find((x) => x.startsWith(`${k}=`)); return a ? a.slice(k.length + 1) : null; };
|
|
32
|
+
|
|
33
|
+
const HOME = val('--home') || os.homedir();
|
|
34
|
+
const NPMRC = path.join(HOME, '.npmrc');
|
|
35
|
+
|
|
36
|
+
const say = (s) => process.stdout.write(`${s}\n`);
|
|
37
|
+
|
|
38
|
+
/** 1 行読む。TTY ならエコーを止めて盗み見を防ぐ(パイプ入力=テストではそのまま読む) */
|
|
39
|
+
function prompt(question) {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: process.stdin.isTTY === true });
|
|
42
|
+
let answered = false;
|
|
43
|
+
const done = (a) => { if (answered) return; answered = true; resolve(a); };
|
|
44
|
+
// stdin が閉じた(EOF・パイプ・Ctrl-D): question のコールバックは来ない。放置すると Promise が解決されず
|
|
45
|
+
// イベントループが空になって**無音で exit 0** する(2026-08-25 change-reviewer が実測で摘発)。
|
|
46
|
+
// 空回答として返し、下の validateTokenShape が「❌ 入力なし」で exit 1 にする。
|
|
47
|
+
rl.on('close', () => { if (!answered) process.stdout.write('\n'); done(''); });
|
|
48
|
+
if (process.stdin.isTTY) {
|
|
49
|
+
// 入力中の文字を画面に出さない(readline の出力を差し替える古典的な方法)
|
|
50
|
+
rl.output.write(question);
|
|
51
|
+
rl._writeToOutput = () => {};
|
|
52
|
+
// done → close の順(close が先だと 'close' ハンドラが空回答で先に resolve してしまう)
|
|
53
|
+
rl.question('', (a) => { done(a); rl.close(); process.stdout.write('\n'); });
|
|
54
|
+
} else {
|
|
55
|
+
rl.question(question, (a) => { done(a); rl.close(); });
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** GitHub Packages に GET して権限を確かめる(応答本文は読み捨て・トークンはヘッダのみ) */
|
|
61
|
+
function probe(token) {
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
// eslint-disable-next-line global-require
|
|
64
|
+
const https = require('https');
|
|
65
|
+
const req = https.request({
|
|
66
|
+
host: 'npm.pkg.github.com', path: W.PROBE_PATH, method: 'GET',
|
|
67
|
+
headers: { authorization: `Bearer ${token}`, accept: 'application/json' }, timeout: 15000,
|
|
68
|
+
}, (res) => { res.resume(); resolve(res.statusCode); });
|
|
69
|
+
req.on('error', () => resolve(null));
|
|
70
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
71
|
+
req.end();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function writeNpmrc(token) {
|
|
76
|
+
// 秘密を書く時の順序(codex R1〜R3 block の封鎖):
|
|
77
|
+
// ① 親ディレクトリが自分の所有で他者書込み不可(差し替え主体を排除)。O_NOFOLLOW / getuid が無い OS は**書かない**
|
|
78
|
+
// ② 既存 .npmrc は O_NOFOLLOW で開き **fd に対して** fstat(通常ファイル・自分の所有・nlink 1)してから内容を読む
|
|
79
|
+
// ③ **既存 inode には書かない**(他者が先に read fd を握っていれば chmod 後も読める・codex R3):
|
|
80
|
+
// 同ディレクトリに O_EXCL|0600 で新 inode を作り → 書く → fsync → rename で置き換える(atomic)
|
|
81
|
+
const dir = path.dirname(NPMRC);
|
|
82
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
83
|
+
if (typeof process.getuid !== 'function' || !fs.constants.O_NOFOLLOW) throw Object.assign(new Error('unsafe-platform'), { code: 'EPLATFORM' });
|
|
84
|
+
const uid = process.getuid();
|
|
85
|
+
const dst = fs.lstatSync(dir);
|
|
86
|
+
if (!dst.isDirectory() || dst.uid !== uid || (dst.mode & 0o022)) throw Object.assign(new Error('parent-dir'), { code: 'EPARENT' });
|
|
87
|
+
const { O_RDONLY, O_WRONLY, O_CREAT, O_EXCL, O_NOFOLLOW } = fs.constants;
|
|
88
|
+
// ② 既存内容(あれば)
|
|
89
|
+
let existing = '';
|
|
90
|
+
let rfd = null;
|
|
91
|
+
try { rfd = fs.openSync(NPMRC, O_RDONLY | O_NOFOLLOW); }
|
|
92
|
+
catch (e) {
|
|
93
|
+
if (e.code === 'ELOOP' || e.code === 'EMLINK') throw Object.assign(new Error('npmrc-symlink'), { code: 'ESYMLINK' });
|
|
94
|
+
if (e.code !== 'ENOENT') throw e;
|
|
95
|
+
}
|
|
96
|
+
if (rfd !== null) {
|
|
97
|
+
try {
|
|
98
|
+
const st = fs.fstatSync(rfd);
|
|
99
|
+
if (!st.isFile()) throw Object.assign(new Error('npmrc-not-file'), { code: 'ENOTFILE' });
|
|
100
|
+
if (st.uid !== uid || st.nlink !== 1) throw Object.assign(new Error('npmrc-not-owned'), { code: 'EOWNER' });
|
|
101
|
+
existing = fs.readFileSync(rfd, 'utf8');
|
|
102
|
+
} finally { fs.closeSync(rfd); }
|
|
103
|
+
}
|
|
104
|
+
// ③ 新 inode に書いて rename
|
|
105
|
+
const tmp = path.join(dir, `.npmrc.${process.pid}.${Date.now()}.tmp`);
|
|
106
|
+
const wfd = fs.openSync(tmp, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0o600);
|
|
107
|
+
try {
|
|
108
|
+
fs.fchmodSync(wfd, 0o600);
|
|
109
|
+
const buf = Buffer.from(W.mergeNpmrc(existing, token), 'utf8');
|
|
110
|
+
let off = 0;
|
|
111
|
+
while (off < buf.length) off += fs.writeSync(wfd, buf, off, buf.length - off, off);
|
|
112
|
+
fs.fsyncSync(wfd);
|
|
113
|
+
} catch (e) { try { fs.unlinkSync(tmp); } catch (_) { /* noop */ } throw e; }
|
|
114
|
+
finally { fs.closeSync(wfd); }
|
|
115
|
+
try { fs.renameSync(tmp, NPMRC); } catch (e) { try { fs.unlinkSync(tmp); } catch (_) { /* noop */ } throw e; }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
(async () => {
|
|
119
|
+
say('');
|
|
120
|
+
say('=== figma-kit セットアップ ===');
|
|
121
|
+
say('');
|
|
122
|
+
say('キット本体は GitHub の限定配布(あなたのアカウント=1 席)です。');
|
|
123
|
+
say('取得用のトークンを 1 回だけ登録します。所要 2 分。');
|
|
124
|
+
say('');
|
|
125
|
+
say('① 次のページを開いて「Generate token」を押してください(read:packages にチェック済みの URL です):');
|
|
126
|
+
say(` ${W.PAT_PAGE}`);
|
|
127
|
+
say('');
|
|
128
|
+
say('② 表示されたトークンをコピーして、ここに貼り付けて Enter(画面には表示されません):');
|
|
129
|
+
|
|
130
|
+
const raw = await prompt(' トークン> ');
|
|
131
|
+
const shape = W.validateTokenShape(raw);
|
|
132
|
+
if (!shape.ok) { say(`\n❌ ${shape.message}`); process.exit(1); }
|
|
133
|
+
const token = shape.token;
|
|
134
|
+
|
|
135
|
+
try { writeNpmrc(token); } catch (e) {
|
|
136
|
+
// エラー文にトークンが載る経路を作らない(メッセージは自前で組む)
|
|
137
|
+
if (e.code === 'ESYMLINK') say(`\n❌ ${NPMRC} がシンボリックリンクです。トークンをリンク先へ書くのは危険なので中止しました。リンクを外してから再実行してください`);
|
|
138
|
+
else if (e.code === 'ENOTFILE') say(`\n❌ ${NPMRC} が通常ファイルではありません。確認してから再実行してください`);
|
|
139
|
+
else if (e.code === 'EOWNER') say(`\n❌ ${NPMRC} があなたの所有でないか、ハードリンクされています。確認してから再実行してください`);
|
|
140
|
+
else if (e.code === 'EPARENT') say(`\n❌ ${path.dirname(NPMRC)} があなたの所有でないか、他のユーザーが書き込めます(安全にトークンを置けません)。権限を直してから再実行してください`);
|
|
141
|
+
else if (e.code === 'EPLATFORM') say('\n❌ この OS では安全にトークンを保存できません(macOS / Linux で実行してください)');
|
|
142
|
+
else say(`\n❌ ${NPMRC} に書けませんでした(${e.code || 'エラー'})。ホームディレクトリの権限を確認してください`);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
say('');
|
|
146
|
+
say(`✅ ${NPMRC} に登録しました(権限 600・${W.redact(token)})`);
|
|
147
|
+
|
|
148
|
+
if (!flag('--no-verify')) {
|
|
149
|
+
say('③ 接続を確認しています…');
|
|
150
|
+
const status = await probe(token);
|
|
151
|
+
const r = W.explainStatus(status);
|
|
152
|
+
if (!r.ok) {
|
|
153
|
+
say(`\n❌ ${r.message}`);
|
|
154
|
+
if (r.next) say(` → ${r.next}`);
|
|
155
|
+
// 再実行の案内: wizard が書いた `@nuwel-dev:registry=npm.pkg.github.com` のせいで素の `npx @nuwel-dev/setup` は
|
|
156
|
+
// GitHub Packages を見に行って 404 になる(codex R1 major)→ scope registry を npmjs に明示して再実行する
|
|
157
|
+
say(` (~/.npmrc には登録済みなので、直したら次で再実行してください)`);
|
|
158
|
+
say(` npx --@nuwel-dev:registry=${W.NPMJS} @nuwel-dev/setup`);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
say(`✅ ${r.message}`);
|
|
162
|
+
} else {
|
|
163
|
+
say('· 接続確認は省略しました(--no-verify)');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (flag('--no-init')) { say('\n· ここまで(--no-init)。次は `npx @nuwel-dev/figma-kit init` を実行してください'); return; }
|
|
167
|
+
|
|
168
|
+
const dir = path.resolve(val('--dir') || process.cwd());
|
|
169
|
+
say('');
|
|
170
|
+
say(`④ 案件を配線します: ${dir}`);
|
|
171
|
+
const r = spawnSync('npx', ['--yes', '@nuwel-dev/figma-kit', 'init', dir], { stdio: 'inherit', env: process.env });
|
|
172
|
+
if (r.status !== 0) {
|
|
173
|
+
say('\n❌ init が失敗しました。上の出力を確認してください(やり直しは `npx @nuwel-dev/figma-kit init`)');
|
|
174
|
+
process.exit(r.status === null ? 1 : r.status);
|
|
175
|
+
}
|
|
176
|
+
say('');
|
|
177
|
+
say('=== 完了 ===');
|
|
178
|
+
say('最初に打つ 1 行: /figma-kit:intake --figma');
|
|
179
|
+
})().catch((e) => {
|
|
180
|
+
// 例外の message にトークンが混ざる可能性を排除するため、種別だけを出す
|
|
181
|
+
say(`\n❌ セットアップが異常終了しました(${(e && e.code) || 'エラー'})`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
});
|
package/lib/wizard.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* @nuwel-dev/setup の純ロジック(2026-08-23・ADR R2 §3.4 W2)。
|
|
4
|
+
*
|
|
5
|
+
* 副作用(stdin 読み・HTTPS・spawn)は bin/setup.js に置き、ここには
|
|
6
|
+
* 「~/.npmrc をどう書き換えるか」「HTTP ステータスを買い手にどう説明するか」だけを置く。
|
|
7
|
+
* 依存ゼロ・Node 20+。**トークンは絶対にログ・stdout へ出さない**(`redact` が唯一の表示口)。
|
|
8
|
+
*/
|
|
9
|
+
const SCOPE = '@nuwel-dev';
|
|
10
|
+
/** 公開側(この橋自身の在処)。wizard が scope を GitHub Packages へ向けた後の再実行は scope registry を明示する */
|
|
11
|
+
const NPMJS = 'https://registry.npmjs.org';
|
|
12
|
+
const REGISTRY = 'https://npm.pkg.github.com';
|
|
13
|
+
const REGISTRY_KEY = '//npm.pkg.github.com/:_authToken';
|
|
14
|
+
const PAT_PAGE = 'https://github.com/settings/tokens/new?scopes=read:packages&description=figma-kit';
|
|
15
|
+
const PROBE_PATH = '/@nuwel-dev%2ffigma-kit';
|
|
16
|
+
|
|
17
|
+
/** 表示用のマスク。**素のトークンを返す経路をここ以外に作らない** */
|
|
18
|
+
function redact(token) {
|
|
19
|
+
const t = String(token || '');
|
|
20
|
+
if (!t) return '(空)';
|
|
21
|
+
return `${t.slice(0, 4)}…${t.slice(-2)}(${t.length} 文字)`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 貼り付けられた値がトークンとして受け取れる形か。
|
|
26
|
+
* 「短すぎる」「空白や改行を含む」は貼り付け事故(途中で切れた・URL を貼った)なので弾く。
|
|
27
|
+
* **正規表現で GitHub の接頭辞を列挙しない** — 接頭辞は増える(ghp_/github_pat_/…)ため、
|
|
28
|
+
* 列挙は必ず「その外側」を穴にする。ここは形だけ見て、真偽の判定は実際の接続確認に委ねる。
|
|
29
|
+
*/
|
|
30
|
+
function validateTokenShape(raw) {
|
|
31
|
+
const t = String(raw == null ? '' : raw).trim();
|
|
32
|
+
if (!t) return { ok: false, message: 'トークンが空です。GitHub の画面でコピーしてから貼り付けてください' };
|
|
33
|
+
if (/\s/.test(t)) return { ok: false, message: 'トークンに空白・改行が混ざっています(URL や複数行を貼っていませんか)' };
|
|
34
|
+
if (t.length < 20) return { ok: false, message: `トークンが短すぎます(${t.length} 文字)。途中で切れていないか確認してください` };
|
|
35
|
+
return { ok: true, token: t };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 既存の ~/.npmrc に「スコープ行」と「authToken 行」を上書き反映する。
|
|
40
|
+
* 他の行(他社レジストリ・proxy 設定など)は 1 行も壊さない。
|
|
41
|
+
* @param {string} existing 既存の中身(無ければ '')
|
|
42
|
+
* @param {string} token
|
|
43
|
+
* @returns {string} 書き戻す中身
|
|
44
|
+
*/
|
|
45
|
+
function mergeNpmrc(existing, token) {
|
|
46
|
+
const keep = String(existing || '')
|
|
47
|
+
.split('\n')
|
|
48
|
+
.filter((l) => {
|
|
49
|
+
const s = l.trim();
|
|
50
|
+
if (!s) return false;
|
|
51
|
+
if (s.startsWith(`${SCOPE}:registry=`)) return false;
|
|
52
|
+
if (s.startsWith(`${REGISTRY_KEY}=`)) return false;
|
|
53
|
+
return true;
|
|
54
|
+
});
|
|
55
|
+
const lines = [...keep, `${SCOPE}:registry=${REGISTRY}`, `${REGISTRY_KEY}=${token}`];
|
|
56
|
+
return `${lines.join('\n')}\n`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 接続確認の HTTP ステータスを**平語**にする。
|
|
61
|
+
* 買い手が最も踏むのは 401(トークンが違う/期限切れ)と 404(招待を受けていない・
|
|
62
|
+
* read:packages が付いていない)で、生のステータスだけ見せると自力で切り分けられない。
|
|
63
|
+
* @param {number|null} status null = 接続そのものができなかった
|
|
64
|
+
* @returns {{ok:boolean, message:string, next:string}}
|
|
65
|
+
*/
|
|
66
|
+
function explainStatus(status) {
|
|
67
|
+
if (status === 200) {
|
|
68
|
+
return { ok: true, message: '接続できました(パッケージの取得権限があります)', next: '' };
|
|
69
|
+
}
|
|
70
|
+
if (status === 401) {
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
message: 'トークンが受け付けられませんでした(401)。値が違うか、期限が切れています',
|
|
74
|
+
next: `${PAT_PAGE} で作り直して、もう一度貼り付けてください`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (status === 403) {
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
message: 'トークンは有効ですが、この配布物を読む権限がありません(403)',
|
|
81
|
+
next: '招待メールを受け取った GitHub アカウントで作ったトークンか確認してください',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (status === 404) {
|
|
85
|
+
return {
|
|
86
|
+
ok: false,
|
|
87
|
+
message: '配布物が見つかりませんでした(404)。ほぼ必ず次のどちらかです: ①招待をまだ Accept していない ②トークンに read:packages の権限が付いていない',
|
|
88
|
+
next: `招待メールの Accept を済ませ、${PAT_PAGE}(read:packages にチェック)で作り直してください`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (status === null) {
|
|
92
|
+
return { ok: false, message: 'ネットワークに出られませんでした(接続失敗)', next: '社内プロキシ・VPN・オフラインでないか確認してください' };
|
|
93
|
+
}
|
|
94
|
+
return { ok: false, message: `想定外の応答でした(HTTP ${status})`, next: 'この行をそのまま販売元へ連絡してください' };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { SCOPE, REGISTRY, NPMJS, REGISTRY_KEY, PAT_PAGE, PROBE_PATH, redact, validateTokenShape, mergeNpmrc, explainStatus };
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nuwel-dev/setup",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "figma-kit のセットアップウィザード(公開 npm・秘密ゼロ)。GitHub Packages のトークンを ~/.npmrc に置き、@nuwel-dev/figma-kit の init へ橋渡しする。",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/nuwel-dev/setup.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://nuwel.jp",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public",
|
|
14
|
+
"provenance": true
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"setup": "bin/setup.js",
|
|
18
|
+
"nuwel-setup": "bin/setup.js"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"bin/",
|
|
25
|
+
"lib/",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"keywords": [
|
|
30
|
+
"figma",
|
|
31
|
+
"setup",
|
|
32
|
+
"nuwel"
|
|
33
|
+
],
|
|
34
|
+
"_design": "依存ゼロ(`dependencies` を持たない)は仕様。これは『private レジストリからは落とせない』鶏と卵を解くためだけの公開パッケージで、秘密を 1 バイトも含まない。依存を足すと供給網が増え、この橋自体が攻撃面になる(ADR R2 §3.4)。provenance は公開レジストリでのみ効くのでここで付ける。",
|
|
35
|
+
"_repository": "公開リポ nuwel-dev/setup(工場 packages/setup からの履歴レス同期先・tools/setup-publish.js)。provenance はソースリポと一致が要るため、限定配布の figma-kit-dist ではなくこちらを指す(2026-08-25・オーナー決裁 A 案)"
|
|
36
|
+
}
|