chiwaa 0.1.1 → 0.1.3
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 +2 -0
- package/bin/chiwaa.js +173 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,6 +18,8 @@ npx chiwaa deploy
|
|
|
18
18
|
| `chiwaa list` | 내 사이트 목록 |
|
|
19
19
|
| `chiwaa versions` | 남아 있는 버전 |
|
|
20
20
|
| `chiwaa rollback [버전]` | 한 단계 전 또는 지정 버전으로 되돌리기 |
|
|
21
|
+
| `chiwaa db` · `db on` · `db create <표>` · `db rows <표>` | 사이트 DB(문의·방명록 등 글 저장) — 켜기·표 만들기·글 보기 |
|
|
22
|
+
| `chiwaa agent` | AI 도구용 안내(AGENTS.md·CLAUDE.md) 만들기 — 처음 deploy 때 자동 |
|
|
21
23
|
| `chiwaa whoami` / `logout` | 계정 확인 / 로그아웃 |
|
|
22
24
|
|
|
23
25
|
## AI 도구(Claude Code·Codex·Cursor)에서
|
package/bin/chiwaa.js
CHANGED
|
@@ -27,7 +27,7 @@ const OUTPUTS = [
|
|
|
27
27
|
["parcel", "dist"],
|
|
28
28
|
];
|
|
29
29
|
// 그대로 올리는 사이트에서 빼는 것
|
|
30
|
-
const SKIP = new Set([".git", "node_modules", ".chiwaa.json", ".DS_Store", "Thumbs.db", ".vercel", ".netlify"]);
|
|
30
|
+
const SKIP = new Set([".git", "node_modules", ".chiwaa.json", ".DS_Store", "Thumbs.db", ".vercel", ".netlify", "AGENTS.md", "CLAUDE.md"]);
|
|
31
31
|
|
|
32
32
|
const c = {
|
|
33
33
|
dim: (s) => (TTY ? `\x1b[2m${s}\x1b[0m` : s),
|
|
@@ -92,7 +92,7 @@ function parseArgs(argv) {
|
|
|
92
92
|
if (a.startsWith("--")) {
|
|
93
93
|
const [k, v] = a.slice(2).split("=", 2);
|
|
94
94
|
if (v !== undefined) out[k] = v;
|
|
95
|
-
else if (["name", "token", "dir"].includes(k) && argv[i + 1] && !argv[i + 1].startsWith("-")) out[k] = argv[++i];
|
|
95
|
+
else if (["name", "token", "dir", "read", "write", "page"].includes(k) && argv[i + 1] && !argv[i + 1].startsWith("-")) out[k] = argv[++i];
|
|
96
96
|
else out[k] = true;
|
|
97
97
|
} else if (a === "-y") out.yes = true;
|
|
98
98
|
else if (a === "-h") out.help = true;
|
|
@@ -307,9 +307,61 @@ function linkSite(dir, site) {
|
|
|
307
307
|
if (site.source === "github")
|
|
308
308
|
fail(`${site.domain} 은 깃허브(${site.repo})로 배포하는 사이트입니다. CLI 로는 다른 이름을 쓰세요: --name <새 이름>`);
|
|
309
309
|
fs.writeFileSync(path.join(dir, LINK_FILE), JSON.stringify({ site: site.name }, null, 2) + "\n");
|
|
310
|
+
writeAgentDoc(dir, site.name, false);
|
|
310
311
|
return site;
|
|
311
312
|
}
|
|
312
313
|
|
|
314
|
+
// ---------- AGENTS.md (AI 코딩 도구가 스스로 읽는 안내) ----------
|
|
315
|
+
// Codex·Cursor 등은 AGENTS.md, Claude Code 는 CLAUDE.md 를 읽는다. 사용자가 쓴 내용은 두고 chiwaa 구간만 넣거나 바꾼다.
|
|
316
|
+
const AGENT_START = "<!-- chiwaa:start -->";
|
|
317
|
+
const AGENT_END = "<!-- chiwaa:end -->";
|
|
318
|
+
|
|
319
|
+
function agentSection(name) {
|
|
320
|
+
return `${AGENT_START}
|
|
321
|
+
## chiwaa 배포 (이 폴더는 ${name}.chiwaa.com 사이트)
|
|
322
|
+
|
|
323
|
+
- 배포: \`npx chiwaa deploy -y\` — 끝나면 주소가 나온다. 잘못 올렸으면 \`npx chiwaa rollback\`.
|
|
324
|
+
- 이 폴더는 \`.chiwaa.json\` 으로 사이트가 정해져 있다. 다른 이름(--name)으로 올리지 않는다.
|
|
325
|
+
- 정적 사이트만 된다(HTML·CSS·JS, Vite·React·Vue·Astro·Next export 빌드). PHP·Express 같은 서버 코드는 안 된다.
|
|
326
|
+
- 글 저장(문의·방명록·게시판)은 사이트 DB: \`npx chiwaa db on\` → \`npx chiwaa db create <표> --read public|owner --write public|owner\`.
|
|
327
|
+
- 문의·신청서처럼 개인정보가 든 표는 반드시 \`--read owner\`. 사이트에서 안 읽힌다고 public 으로 바꾸지 않는다.
|
|
328
|
+
- 방문자 글은 화면에 \`textContent\` 로만 넣는다. 비밀번호는 \`<input type="password">\` 로 받는다.
|
|
329
|
+
- 로그인 토큰(~/.chiwaa/config.json, CHIWAA_TOKEN)은 출력하거나 파일·저장소에 적지 않는다.
|
|
330
|
+
- 자세한 규칙·예시: https://chiwaa.kr/llms.txt — 처음 작업하거나 헷갈리면 먼저 읽는다.
|
|
331
|
+
${AGENT_END}`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// 바뀐 파일 이름 목록을 돌려준다
|
|
335
|
+
function writeAgentDoc(dir, name, loud) {
|
|
336
|
+
const changed = [];
|
|
337
|
+
const agents = path.join(dir, "AGENTS.md");
|
|
338
|
+
const block = agentSection(name);
|
|
339
|
+
let text = fs.existsSync(agents) ? fs.readFileSync(agents, "utf8") : "";
|
|
340
|
+
const re = new RegExp(`${AGENT_START}[\\s\\S]*?${AGENT_END}`);
|
|
341
|
+
const next = re.test(text) ? text.replace(re, block) : (text ? `${text.replace(/\s*$/, "")}\n\n${block}\n` : `${block}\n`);
|
|
342
|
+
if (next !== text) {
|
|
343
|
+
fs.writeFileSync(agents, next);
|
|
344
|
+
changed.push("AGENTS.md");
|
|
345
|
+
}
|
|
346
|
+
// Claude Code 는 CLAUDE.md 를 읽는다 — 없을 때만 AGENTS.md 를 불러오는 한 줄짜리를 만든다
|
|
347
|
+
const claude = path.join(dir, "CLAUDE.md");
|
|
348
|
+
if (!fs.existsSync(claude)) {
|
|
349
|
+
fs.writeFileSync(claude, "@AGENTS.md\n");
|
|
350
|
+
changed.push("CLAUDE.md");
|
|
351
|
+
} else if (!/@AGENTS\.md|chiwaa:start/.test(fs.readFileSync(claude, "utf8"))) {
|
|
352
|
+
fs.appendFileSync(claude, "\n@AGENTS.md\n");
|
|
353
|
+
changed.push("CLAUDE.md");
|
|
354
|
+
}
|
|
355
|
+
if (changed.length) console.log(c.dim(`AI 도구용 안내를 ${changed.join("·")} 에 적었습니다(사이트에는 올라가지 않음).`));
|
|
356
|
+
else if (loud) console.log("AI 도구용 안내가 이미 최신입니다(AGENTS.md).");
|
|
357
|
+
return changed;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async function agent(args) {
|
|
361
|
+
const name = await siteName(args);
|
|
362
|
+
writeAgentDoc(process.cwd(), name, true);
|
|
363
|
+
}
|
|
364
|
+
|
|
313
365
|
// ---------- deploy ----------
|
|
314
366
|
async function deploy(args) {
|
|
315
367
|
const dir = path.resolve(args._[1] || ".");
|
|
@@ -369,6 +421,115 @@ async function rollback(args) {
|
|
|
369
421
|
console.log(c.green("✓"), r.data.message);
|
|
370
422
|
}
|
|
371
423
|
|
|
424
|
+
// ---------- db (사이트 DB) ----------
|
|
425
|
+
// 데이터가 지워지는 일(끄기·표 지우기·글 지우기)은 일부러 없다 — chiwaa.kr 사이트 화면에서만 한다.
|
|
426
|
+
const RULE_KO = { public: "누구나", owner: "나만(chiwaa.kr 화면)" };
|
|
427
|
+
|
|
428
|
+
function snippet(table) {
|
|
429
|
+
return `<!-- 글 보내기(저장) -->
|
|
430
|
+
<script src="/_chiwaa/data/v1/chiwaa.js"></script>
|
|
431
|
+
<form data-chiwaa-table="${table}" data-chiwaa-done="보냈습니다. 감사합니다.">
|
|
432
|
+
<input name="이름" placeholder="이름" required>
|
|
433
|
+
<textarea name="내용" placeholder="내용" required></textarea>
|
|
434
|
+
<input name="_pw" type="password" placeholder="비밀번호(내 글 수정·삭제용, 4자 이상)">
|
|
435
|
+
<input name="_hp" style="display:none" tabindex="-1" autocomplete="off">
|
|
436
|
+
<button type="submit">보내기</button>
|
|
437
|
+
</form>
|
|
438
|
+
|
|
439
|
+
<!-- 글 목록 보기(읽기 권한이 public 일 때) -->
|
|
440
|
+
<ul id="${table}-list"></ul>
|
|
441
|
+
<script>
|
|
442
|
+
chiwaa.table("${table}").list({ limit: 20 }).then(function (items) {
|
|
443
|
+
var ul = document.getElementById("${table}-list");
|
|
444
|
+
items.forEach(function (item) {
|
|
445
|
+
var li = document.createElement("li");
|
|
446
|
+
li.textContent = (item["이름"] || "") + ": " + (item["내용"] || ""); // 방문자 글은 textContent 로만
|
|
447
|
+
ul.appendChild(li);
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
</script>
|
|
451
|
+
|
|
452
|
+
<!-- 스크립트로 직접:
|
|
453
|
+
chiwaa.table("${table}").add({ 제목, 내용, _pw }) 글쓰기(_pw 를 넣으면 그 비밀번호로 수정·삭제 가능)
|
|
454
|
+
chiwaa.table("${table}").list({ limit: 20, before: 마지막id }) 목록(최신순, 다음 쪽은 before)
|
|
455
|
+
chiwaa.table("${table}").get(id) 글 하나(상세 화면)
|
|
456
|
+
chiwaa.table("${table}").update(id, { 제목, 내용 }, 비밀번호) 글쓴이가 고치기
|
|
457
|
+
chiwaa.table("${table}").remove(id, 비밀번호) 글쓴이가 지우기
|
|
458
|
+
댓글은 표를 하나 더 만들고(예: comments) 글 번호를 함께 저장 → .list({ where: { post_id: id } }) -->`;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function ruleArg(v, label) {
|
|
462
|
+
if (v === undefined || v === true) return undefined;
|
|
463
|
+
if (!["public", "owner"].includes(v)) fail(`${label} 는 public(누구나) 또는 owner(나만) 입니다: ${v}`);
|
|
464
|
+
return v;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
async function db(args) {
|
|
468
|
+
const sub = args._[1] || "status";
|
|
469
|
+
const name = await siteName(args);
|
|
470
|
+
const base = `/api/v1/sites/${encodeURIComponent(name)}/db`;
|
|
471
|
+
const table = args._[2];
|
|
472
|
+
const needTable = () => {
|
|
473
|
+
if (!table) fail(`표 이름을 적어 주세요. 예: npx chiwaa db ${sub} guestbook`);
|
|
474
|
+
if (!/^[a-z][a-z0-9_]{0,39}$/.test(table)) fail(`표 이름은 영문 소문자로 시작하고 영문 소문자·숫자·밑줄(_) 40자까지입니다: ${table}`);
|
|
475
|
+
return table;
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
if (sub === "status") {
|
|
479
|
+
const r = await api("GET", base);
|
|
480
|
+
if (r.status !== 200) fail(r.data.error || "DB 정보를 받지 못했습니다.");
|
|
481
|
+
if (!r.data.enabled) return console.log(`${name}.chiwaa.com 의 DB는 꺼져 있습니다. 켜려면: npx chiwaa db on`);
|
|
482
|
+
console.log(c.bold(`${name}.chiwaa.com DB`), c.dim(`· 쓴 양 ${(r.data.bytes / 1048576).toFixed(1)}MB / ${r.data.limit_mb}MB · 오늘 저장 ${r.data.writes_today}/${r.data.limit_writes}`));
|
|
483
|
+
if (!r.data.tables.length) return console.log("아직 표가 없습니다. 예: npx chiwaa db create guestbook --read public --write public");
|
|
484
|
+
for (const t of r.data.tables)
|
|
485
|
+
console.log(` ${t.name} ${c.dim(`읽기 ${RULE_KO[t.read_rule]} · 쓰기 ${RULE_KO[t.write_rule]} · 글 ${t.records}개`)}`);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (sub === "on") {
|
|
489
|
+
const r = await api("POST", `${base}/on`);
|
|
490
|
+
if (r.status !== 200) fail(r.data.error || "DB를 켜지 못했습니다.");
|
|
491
|
+
return console.log(c.green("✓"), r.data.message);
|
|
492
|
+
}
|
|
493
|
+
if (sub === "create") {
|
|
494
|
+
needTable();
|
|
495
|
+
const read = ruleArg(args.read, "--read") || "owner";
|
|
496
|
+
const write = ruleArg(args.write, "--write") || "public";
|
|
497
|
+
const r = await api("POST", `${base}/tables`, { body: { table, read, write } });
|
|
498
|
+
if (r.status !== 201) fail(r.data.error || "표를 만들지 못했습니다.");
|
|
499
|
+
console.log(c.green("✓"), `「${table}」 표를 만들었습니다. 읽기 ${RULE_KO[read]} · 쓰기 ${RULE_KO[write]}`);
|
|
500
|
+
console.log(c.dim("\n사이트 HTML 에 아래를 넣고 다시 deploy 하면 됩니다. 저장된 글은 npx chiwaa db rows " + table + "\n"));
|
|
501
|
+
return console.log(snippet(table));
|
|
502
|
+
}
|
|
503
|
+
if (sub === "rules") {
|
|
504
|
+
needTable();
|
|
505
|
+
const read = ruleArg(args.read, "--read");
|
|
506
|
+
const write = ruleArg(args.write, "--write");
|
|
507
|
+
if (!read && !write) fail("바꿀 권한을 --read public|owner 나 --write public|owner 로 알려 주세요.");
|
|
508
|
+
const r = await api("POST", `${base}/tables/${table}/rules`, { body: { read, write } });
|
|
509
|
+
if (r.status !== 200) fail(r.data.error || "권한을 바꾸지 못했습니다.");
|
|
510
|
+
return console.log(c.green("✓"), `「${table}」 읽기 ${RULE_KO[r.data.read_rule]} · 쓰기 ${RULE_KO[r.data.write_rule]}`);
|
|
511
|
+
}
|
|
512
|
+
if (sub === "rows") {
|
|
513
|
+
needTable();
|
|
514
|
+
const page = Math.max(parseInt(args.page, 10) || 1, 1);
|
|
515
|
+
const r = await api("GET", `${base}/tables/${table}/rows?page=${page}`);
|
|
516
|
+
if (r.status !== 200) fail(r.data.error || "글을 받지 못했습니다.");
|
|
517
|
+
const items = r.data.items || [];
|
|
518
|
+
if (!items.length) return console.log(page > 1 ? "이 쪽에는 글이 없습니다." : "아직 저장된 글이 없습니다.");
|
|
519
|
+
for (const it of items) {
|
|
520
|
+
const { id, created_at, _editable, ...rest } = it;
|
|
521
|
+
const d = new Date(created_at);
|
|
522
|
+
const p2 = (n) => String(n).padStart(2, "0");
|
|
523
|
+
const when = isNaN(d) ? String(created_at) : `${d.getFullYear()}-${p2(d.getMonth() + 1)}-${p2(d.getDate())} ${p2(d.getHours())}:${p2(d.getMinutes())}`;
|
|
524
|
+
console.log(`${c.dim(`#${id} ${when}`)} ${JSON.stringify(rest)}`);
|
|
525
|
+
}
|
|
526
|
+
if (items.length >= 20) console.log(c.dim(`다음 쪽: npx chiwaa db rows ${table} --page ${page + 1}`));
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (sub === "code") return console.log(snippet(needTable()));
|
|
530
|
+
fail(`모르는 db 명령입니다: ${sub}\n 쓸 수 있는 것: status · on · create · rules · rows · code`);
|
|
531
|
+
}
|
|
532
|
+
|
|
372
533
|
async function whoami() {
|
|
373
534
|
const r = await api("GET", "/api/v1/me");
|
|
374
535
|
if (r.status !== 200) fail(r.data.error || "로그인 정보를 받지 못했습니다.");
|
|
@@ -393,6 +554,15 @@ const HELP = `chiwaa ${VERSION} — chiwaa.kr 에 사이트를 바로 올립니
|
|
|
393
554
|
chiwaa list 내 사이트 목록
|
|
394
555
|
chiwaa versions 남아 있는 버전 보기
|
|
395
556
|
chiwaa rollback [버전] 한 단계 전(또는 지정 버전)으로 되돌리기
|
|
557
|
+
chiwaa db 사이트 DB 상태·표 목록
|
|
558
|
+
chiwaa db on DB 켜기
|
|
559
|
+
chiwaa db create <표> 표 만들기 (--read public|owner, --write public|owner)
|
|
560
|
+
예: chiwaa db create guestbook --read public --write public
|
|
561
|
+
chiwaa db rules <표> 읽기·쓰기 권한 바꾸기
|
|
562
|
+
chiwaa db rows <표> 저장된 글 보기 (--page 2)
|
|
563
|
+
chiwaa db code <표> 사이트에 붙일 코드 다시 보기
|
|
564
|
+
chiwaa agent AI 도구용 안내(AGENTS.md·CLAUDE.md)를 만들거나 새로 고침
|
|
565
|
+
(처음 deploy 할 때 자동으로 만든다)
|
|
396
566
|
chiwaa whoami 로그인한 계정
|
|
397
567
|
chiwaa logout 로그아웃
|
|
398
568
|
|
|
@@ -404,7 +574,7 @@ async function main() {
|
|
|
404
574
|
const cmd = args._[0];
|
|
405
575
|
if (args.version) return console.log(VERSION);
|
|
406
576
|
if (args.help || !cmd || cmd === "help") return console.log(HELP);
|
|
407
|
-
const cmds = { deploy, login, list, ls: list, versions, rollback, whoami, logout };
|
|
577
|
+
const cmds = { deploy, login, list, ls: list, versions, rollback, db, agent, whoami, logout };
|
|
408
578
|
if (!cmds[cmd]) fail(`모르는 명령입니다: ${cmd}\n\n${HELP}`);
|
|
409
579
|
await cmds[cmd](args);
|
|
410
580
|
}
|