@pplethai/cli 0.4.0 → 0.4.2
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 +1 -1
- package/dist/bin.js +53 -15
- package/package.json +3 -2
- package/skills/pple-platform/references/design-system.md +7 -2
- package/templates/golden-path/package.json +1 -0
- package/templates/golden-path/web/App.tsx +129 -49
- package/templates/golden-path/web/app.css +13 -22
- package/templates/golden-path/web/main.tsx +1 -1
- package/templates/golden-path/web/pple.ts +13 -2
- package/dist/api.js +0 -108
- package/dist/cli.js +0 -151
- package/dist/commands/auth.js +0 -32
- package/dist/commands/deploy.js +0 -113
- package/dist/commands/env.js +0 -70
- package/dist/commands/info.js +0 -238
- package/dist/commands/init.js +0 -159
- package/dist/commands/upgrade.js +0 -29
- package/dist/credentials.js +0 -53
- package/dist/deps.js +0 -87
- package/dist/device-flow.js +0 -122
- package/dist/errors.js +0 -16
- package/dist/git.js +0 -127
- package/dist/link.js +0 -59
- package/dist/node-deps.js +0 -107
- package/dist/npm.js +0 -127
- package/dist/scaffold.js +0 -58
- package/dist/session.js +0 -29
- package/dist/skills.js +0 -86
- package/dist/ui.js +0 -12
- package/dist/update.js +0 -142
package/dist/skills.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
// Platform Skills as a local overlay in each Builder App workspace.
|
|
2
|
-
// Overwrites only `.claude/skills/pple-platform/`; sibling skills are left alone.
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
|
-
import fs from "node:fs/promises";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { CliError } from "./errors.js";
|
|
7
|
-
import { MANUAL_INSTALL } from "./npm.js";
|
|
8
|
-
/** Where the platform Skill lands inside a Builder App workspace. */
|
|
9
|
-
export const PLATFORM_SKILL_REL = path.join(".claude", "skills", "pple-platform");
|
|
10
|
-
/** The only Skills path that belongs in the app's `.gitignore` (posix form). */
|
|
11
|
-
export const PLATFORM_SKILL_IGNORE = `${PLATFORM_SKILL_REL.split(path.sep).join("/")}/`;
|
|
12
|
-
/**
|
|
13
|
-
* Refresh the platform Skill overlay from the CLI bundle. Prints only when
|
|
14
|
-
* the managed content actually changed (first install or update).
|
|
15
|
-
*/
|
|
16
|
-
export async function refreshSkills(deps, appDir) {
|
|
17
|
-
const source = deps.skillsDir;
|
|
18
|
-
const entries = await fs.readdir(source).catch(() => null);
|
|
19
|
-
if (entries === null) {
|
|
20
|
-
throw new CliError("ไม่พบ Skill ของ PPLE Platform ในการติดตั้ง CLI ชุดนี้", `ติดตั้ง CLI ใหม่ (\`${MANUAL_INSTALL}\`) แล้วลองอีกครั้ง`);
|
|
21
|
-
}
|
|
22
|
-
const destination = path.join(appDir, PLATFORM_SKILL_REL);
|
|
23
|
-
const before = await contentFingerprint(destination);
|
|
24
|
-
await replaceDir(source, destination);
|
|
25
|
-
await ensureIgnored(appDir);
|
|
26
|
-
const after = await contentFingerprint(destination);
|
|
27
|
-
if (before === after)
|
|
28
|
-
return;
|
|
29
|
-
deps.out.say(before === null ? "ติดตั้ง Skill ของแพลตฟอร์มแล้ว" : "อัปเดต Skill ของแพลตฟอร์มแล้ว");
|
|
30
|
-
}
|
|
31
|
-
async function replaceDir(from, to) {
|
|
32
|
-
await fs.rm(to, { recursive: true, force: true });
|
|
33
|
-
await copyDir(from, to);
|
|
34
|
-
}
|
|
35
|
-
async function copyDir(from, to) {
|
|
36
|
-
await fs.mkdir(to, { recursive: true });
|
|
37
|
-
for (const entry of await fs.readdir(from, { withFileTypes: true })) {
|
|
38
|
-
if (entry.name === ".DS_Store")
|
|
39
|
-
continue;
|
|
40
|
-
const src = path.join(from, entry.name);
|
|
41
|
-
const dst = path.join(to, entry.name);
|
|
42
|
-
if (entry.isDirectory())
|
|
43
|
-
await copyDir(src, dst);
|
|
44
|
-
else
|
|
45
|
-
await fs.copyFile(src, dst);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
/** Stable hash of every file under `dir`, or null when the tree is missing. */
|
|
49
|
-
async function contentFingerprint(dir) {
|
|
50
|
-
const files = await listFiles(dir).catch(() => null);
|
|
51
|
-
if (files === null)
|
|
52
|
-
return null;
|
|
53
|
-
const hash = createHash("sha256");
|
|
54
|
-
for (const relative of files.sort()) {
|
|
55
|
-
hash.update(relative);
|
|
56
|
-
hash.update("\0");
|
|
57
|
-
hash.update(await fs.readFile(path.join(dir, relative)));
|
|
58
|
-
hash.update("\0");
|
|
59
|
-
}
|
|
60
|
-
return hash.digest("hex");
|
|
61
|
-
}
|
|
62
|
-
async function listFiles(dir, prefix = "") {
|
|
63
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
64
|
-
const files = [];
|
|
65
|
-
for (const entry of entries) {
|
|
66
|
-
if (entry.name === ".DS_Store")
|
|
67
|
-
continue;
|
|
68
|
-
const relative = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
69
|
-
if (entry.isDirectory()) {
|
|
70
|
-
files.push(...(await listFiles(path.join(dir, entry.name), relative)));
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
files.push(relative);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return files;
|
|
77
|
-
}
|
|
78
|
-
async function ensureIgnored(appDir) {
|
|
79
|
-
const ignorePath = path.join(appDir, ".gitignore");
|
|
80
|
-
const existing = await fs.readFile(ignorePath, "utf8").catch(() => "");
|
|
81
|
-
const lines = existing.split("\n");
|
|
82
|
-
if (lines.some((line) => line.trim() === PLATFORM_SKILL_IGNORE))
|
|
83
|
-
return;
|
|
84
|
-
const body = existing.endsWith("\n") || existing === "" ? existing : `${existing}\n`;
|
|
85
|
-
await fs.writeFile(ignorePath, `${body}${PLATFORM_SKILL_IGNORE}\n`);
|
|
86
|
-
}
|
package/dist/ui.js
DELETED
package/dist/update.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
// The CLI keeping itself current: the daily check that rides along with
|
|
2
|
-
// ordinary commands, and the words both it and `pple upgrade` use — one
|
|
3
|
-
// upgrade story reaches a Builder, however they arrived at it.
|
|
4
|
-
//
|
|
5
|
-
// The check runs before the command the Builder typed, which is the only
|
|
6
|
-
// moment an upgrade is safe: nothing is half-done yet, so the new CLI can
|
|
7
|
-
// simply be handed the same command line. Everything here is written to fail
|
|
8
|
-
// towards the command — a quiet npm, a missing config directory, a refused
|
|
9
|
-
// upgrade all end with `pple deploy` deploying.
|
|
10
|
-
import fs from "node:fs/promises";
|
|
11
|
-
import path from "node:path";
|
|
12
|
-
import { installGlobally, isNewer, isNpmGlobalInstall, latestVersion, MANUAL_INSTALL, npmMessage, } from "./npm.js";
|
|
13
|
-
/** About once a day, per machine — not per app, and not per command. */
|
|
14
|
-
const CHECK_INTERVAL = 24 * 60 * 60 * 1000;
|
|
15
|
-
/** Cadence state, beside credentials.json and well away from any app repo. */
|
|
16
|
-
const STATE_FILE = "update-check.json";
|
|
17
|
-
/** The Builder's own settings for this CLI, the other half of the opt-out. */
|
|
18
|
-
const CONFIG_FILE = "config.json";
|
|
19
|
-
/**
|
|
20
|
-
* Commands that already answer the version question themselves, or that
|
|
21
|
-
* print and exit. `upgrade` doing its own check twice would be silly; help
|
|
22
|
-
* should never touch the network.
|
|
23
|
-
*/
|
|
24
|
-
const UNCHECKED = new Set(["upgrade", "help", "--help", "-h"]);
|
|
25
|
-
export function wantsUpdateCheck(argv) {
|
|
26
|
-
return argv.length > 0 && !UNCHECKED.has(argv[0]);
|
|
27
|
-
}
|
|
28
|
-
/** The one line that opens every "there's a newer pple" conversation. */
|
|
29
|
-
export function announceUpdate(deps, latest) {
|
|
30
|
-
deps.out.say(`มี pple เวอร์ชันใหม่ (${latest}) คุณใช้เวอร์ชัน ${deps.version}`);
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* What to say about an install npm's global root doesn't hold. Installing
|
|
34
|
-
* over it would leave the Builder with two pples and no way to tell which
|
|
35
|
-
* one just ran (ADR-0007).
|
|
36
|
-
*/
|
|
37
|
-
export function sayOtherChannel(deps) {
|
|
38
|
-
deps.out.say("pple ชุดนี้ไม่ได้ติดตั้งผ่าน npm การอัปเดตที่นี่อาจทำให้คุณ");
|
|
39
|
-
deps.out.say("มี pple สองเวอร์ชัน กรุณาอัปเดตด้วยวิธีเดิมที่คุณติดตั้งไว้");
|
|
40
|
-
deps.out.say(`หรือติดตั้งด้วย npm:\n\n ${MANUAL_INSTALL}`);
|
|
41
|
-
}
|
|
42
|
-
/** Why npm gave up, and the line the Builder can always run themselves. */
|
|
43
|
-
export function manualHint(result) {
|
|
44
|
-
return `${npmMessage(result)}\n\nลองรันคำสั่งนี้ด้วยตัวเอง:\n\n ${MANUAL_INSTALL}`;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Look for a newer CLI, and act on it as far as this session allows. Returns
|
|
48
|
-
* an exit code when the command has already been dealt with — the CLI
|
|
49
|
-
* upgraded and re-ran it, or the Builder chose to stop — and null to let the
|
|
50
|
-
* command the Builder typed run as usual.
|
|
51
|
-
*/
|
|
52
|
-
export async function checkForUpdate(deps, argv) {
|
|
53
|
-
if (!(await isDue(deps)))
|
|
54
|
-
return null;
|
|
55
|
-
await recordCheck(deps);
|
|
56
|
-
const latest = await latestVersion(deps);
|
|
57
|
-
// A quiet registry is not the Builder's problem: no message, no delay
|
|
58
|
-
// tomorrow — the command simply carries on.
|
|
59
|
-
if (latest === null || !isNewer(latest, deps.version))
|
|
60
|
-
return null;
|
|
61
|
-
announceUpdate(deps, latest);
|
|
62
|
-
// The channel comes before the terminal: an install npm doesn't own can't
|
|
63
|
-
// be upgraded by anyone here, so pointing at `pple upgrade` — which would
|
|
64
|
-
// only refuse — helps nobody, attended or not.
|
|
65
|
-
if (!(await isNpmGlobalInstall(deps))) {
|
|
66
|
-
sayOtherChannel(deps);
|
|
67
|
-
deps.out.say();
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
if (!deps.interactive) {
|
|
71
|
-
deps.out.say("รัน `pple upgrade` เมื่อคุณพร้อม");
|
|
72
|
-
deps.out.say();
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
if (!(await deps.confirm("อัปเดต pple ตอนนี้เลยไหม?"))) {
|
|
76
|
-
deps.out.say();
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
const result = await installGlobally(deps, latest);
|
|
80
|
-
if (result.code !== 0)
|
|
81
|
-
return await afterFailedUpgrade(deps, latest, result);
|
|
82
|
-
deps.out.say(`pple เป็นเวอร์ชัน ${latest} แล้ว กำลังทำงานต่อจากที่ค้างไว้…`);
|
|
83
|
-
deps.out.say();
|
|
84
|
-
return deps.reexec(argv);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* A failed upgrade must not strand the work the Builder came to do. Say what
|
|
88
|
-
* npm said, leave them the line they can run themselves, and offer to get on
|
|
89
|
-
* with the command on the CLI they already have. Only an attended run can
|
|
90
|
-
* arrive here at all — an unattended one is never upgrading in the first
|
|
91
|
-
* place — so carrying on is a question, not a default.
|
|
92
|
-
*/
|
|
93
|
-
async function afterFailedUpgrade(deps, latest, result) {
|
|
94
|
-
deps.out.say(`อัปเดต pple เป็น ${latest} ไม่สำเร็จ`);
|
|
95
|
-
deps.out.say(manualHint(result));
|
|
96
|
-
deps.out.say();
|
|
97
|
-
if (await deps.confirm(`ใช้ pple ${deps.version} ทำงานต่อไปก่อนไหม?`)) {
|
|
98
|
-
deps.out.say();
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
deps.out.say("หยุดแล้ว ไม่มีอะไรเปลี่ยนแปลง");
|
|
102
|
-
return 1;
|
|
103
|
-
}
|
|
104
|
-
async function isDue(deps) {
|
|
105
|
-
if (!deps.updateChecks)
|
|
106
|
-
return false;
|
|
107
|
-
if (!(await configAllowsChecks(deps)))
|
|
108
|
-
return false;
|
|
109
|
-
const state = await readJson(path.join(deps.configDir, STATE_FILE));
|
|
110
|
-
if (typeof state?.lastCheckedAt !== "number")
|
|
111
|
-
return true;
|
|
112
|
-
return deps.now() - state.lastCheckedAt >= CHECK_INTERVAL;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Record the attempt, not the outcome: a registry that is down today should
|
|
116
|
-
* cost one request, not one per command until it comes back. Unwritable
|
|
117
|
-
* config is not worth a word to the Builder — checking again is harmless.
|
|
118
|
-
*/
|
|
119
|
-
async function recordCheck(deps) {
|
|
120
|
-
const state = { lastCheckedAt: deps.now() };
|
|
121
|
-
await fs
|
|
122
|
-
.mkdir(deps.configDir, { recursive: true })
|
|
123
|
-
.then(() => fs.writeFile(path.join(deps.configDir, STATE_FILE), `${JSON.stringify(state, null, 2)}\n`))
|
|
124
|
-
.catch(() => undefined);
|
|
125
|
-
}
|
|
126
|
-
/** `{ "updateCheck": false }` turns the check off for this machine. */
|
|
127
|
-
async function configAllowsChecks(deps) {
|
|
128
|
-
const config = await readJson(path.join(deps.configDir, CONFIG_FILE));
|
|
129
|
-
return config?.updateCheck !== false;
|
|
130
|
-
}
|
|
131
|
-
/** Parsed JSON, or null for a file that is missing, unreadable, or malformed. */
|
|
132
|
-
async function readJson(file) {
|
|
133
|
-
const raw = await fs.readFile(file, "utf8").catch(() => null);
|
|
134
|
-
if (raw === null)
|
|
135
|
-
return null;
|
|
136
|
-
try {
|
|
137
|
-
return JSON.parse(raw);
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
}
|