@alook/cli 0.0.1 → 0.0.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/index.js +158 -42
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -145,6 +145,137 @@ function saveCLIConfigForProfile(profile, profileConfig) {
|
|
|
145
145
|
saveCLIConfig(cfg);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
// lib/installer.ts
|
|
149
|
+
import { spawnSync } from "child_process";
|
|
150
|
+
|
|
151
|
+
// lib/version.ts
|
|
152
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
153
|
+
import { join as join2, dirname } from "path";
|
|
154
|
+
import { fileURLToPath } from "url";
|
|
155
|
+
function getCurrentVersion() {
|
|
156
|
+
const __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
157
|
+
const candidates = [
|
|
158
|
+
join2(__dirname2, "..", "package.json"),
|
|
159
|
+
join2(__dirname2, "..", "..", "package.json")
|
|
160
|
+
];
|
|
161
|
+
for (const candidate of candidates) {
|
|
162
|
+
try {
|
|
163
|
+
const pkg = JSON.parse(readFileSync2(candidate, "utf-8"));
|
|
164
|
+
if (typeof pkg.version === "string")
|
|
165
|
+
return pkg.version;
|
|
166
|
+
} catch {}
|
|
167
|
+
}
|
|
168
|
+
return "unknown";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// lib/installer.ts
|
|
172
|
+
var PACKAGE = "@alook/cli";
|
|
173
|
+
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE}/latest`;
|
|
174
|
+
var FETCH_TIMEOUT_MS = 3000;
|
|
175
|
+
async function fetchLatestVersion() {
|
|
176
|
+
const ctrl = new AbortController;
|
|
177
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
|
|
178
|
+
try {
|
|
179
|
+
const res = await fetch(REGISTRY_URL, { signal: ctrl.signal });
|
|
180
|
+
if (!res.ok)
|
|
181
|
+
return null;
|
|
182
|
+
const data = await res.json();
|
|
183
|
+
return typeof data.version === "string" ? data.version : null;
|
|
184
|
+
} catch {
|
|
185
|
+
return null;
|
|
186
|
+
} finally {
|
|
187
|
+
clearTimeout(timer);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function isNewer(current, latest) {
|
|
191
|
+
const parse = (v) => v.split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
192
|
+
const [cMaj = 0, cMin = 0, cPatch = 0] = parse(current);
|
|
193
|
+
const [lMaj = 0, lMin = 0, lPatch = 0] = parse(latest);
|
|
194
|
+
if (lMaj !== cMaj)
|
|
195
|
+
return lMaj > cMaj;
|
|
196
|
+
if (lMin !== cMin)
|
|
197
|
+
return lMin > cMin;
|
|
198
|
+
return lPatch > cPatch;
|
|
199
|
+
}
|
|
200
|
+
function isNpx() {
|
|
201
|
+
return process.env.npm_command === "exec" || !!process.env.npm_execpath?.includes("npx-cli");
|
|
202
|
+
}
|
|
203
|
+
function detectPackageManager() {
|
|
204
|
+
const ua = process.env.npm_config_user_agent || "";
|
|
205
|
+
if (ua.startsWith("pnpm/"))
|
|
206
|
+
return "pnpm";
|
|
207
|
+
if (ua.startsWith("yarn/"))
|
|
208
|
+
return "yarn";
|
|
209
|
+
return "npm";
|
|
210
|
+
}
|
|
211
|
+
function installArgs(pm) {
|
|
212
|
+
switch (pm) {
|
|
213
|
+
case "pnpm":
|
|
214
|
+
return ["pnpm", ["add", "-g", PACKAGE]];
|
|
215
|
+
case "yarn":
|
|
216
|
+
return ["yarn", ["global", "add", PACKAGE]];
|
|
217
|
+
case "npm":
|
|
218
|
+
return ["npm", ["install", "-g", `${PACKAGE}@latest`]];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function installCmdString(pm) {
|
|
222
|
+
const [bin, args] = installArgs(pm);
|
|
223
|
+
return `${bin} ${args.join(" ")}`;
|
|
224
|
+
}
|
|
225
|
+
function runInstall(pm) {
|
|
226
|
+
const [bin, args] = installArgs(pm);
|
|
227
|
+
const result = spawnSync(bin, args, { stdio: "inherit" });
|
|
228
|
+
return result.status === 0;
|
|
229
|
+
}
|
|
230
|
+
async function ensureInstalled(opts = {}, deps = {}) {
|
|
231
|
+
const fetchLatest = deps.fetchLatest ?? fetchLatestVersion;
|
|
232
|
+
const runInst = deps.runInstall ?? runInstall;
|
|
233
|
+
const getCurrent = deps.getCurrent ?? getCurrentVersion;
|
|
234
|
+
const isNpxFn = deps.isNpxFn ?? isNpx;
|
|
235
|
+
const isDevFn = deps.isDevFn ?? isDev;
|
|
236
|
+
const log = deps.log ?? ((m) => console.log(m));
|
|
237
|
+
const current = getCurrent();
|
|
238
|
+
const pm = detectPackageManager();
|
|
239
|
+
if (isDevFn() || opts.skip) {
|
|
240
|
+
return { skipped: true, action: "none", current, latest: null, pm };
|
|
241
|
+
}
|
|
242
|
+
const latest = await fetchLatest();
|
|
243
|
+
if (!latest) {
|
|
244
|
+
return { skipped: false, action: "none", current, latest: null, pm };
|
|
245
|
+
}
|
|
246
|
+
const runningViaNpx = isNpxFn();
|
|
247
|
+
const needsInstall = runningViaNpx;
|
|
248
|
+
const needsUpdate = !runningViaNpx && isNewer(current, latest);
|
|
249
|
+
if (!needsInstall && !needsUpdate) {
|
|
250
|
+
log(`
|
|
251
|
+
✓ ${PACKAGE} is up to date (${current})`);
|
|
252
|
+
return { skipped: false, action: "none", current, latest, pm };
|
|
253
|
+
}
|
|
254
|
+
const cmdStr = installCmdString(pm);
|
|
255
|
+
if (needsInstall) {
|
|
256
|
+
log(`
|
|
257
|
+
Installing ${PACKAGE} globally (${cmdStr})...`);
|
|
258
|
+
} else {
|
|
259
|
+
log(`
|
|
260
|
+
Updating ${PACKAGE} ${current} → ${latest} (${cmdStr})...`);
|
|
261
|
+
}
|
|
262
|
+
const ok = runInst(pm);
|
|
263
|
+
if (!ok) {
|
|
264
|
+
log(`
|
|
265
|
+
Could not ${needsInstall ? "install" : "update"} ${PACKAGE} automatically.`);
|
|
266
|
+
log(`Install it manually: ${cmdStr}`);
|
|
267
|
+
return { skipped: false, action: "failed", current, latest, pm };
|
|
268
|
+
}
|
|
269
|
+
log(`✓ ${needsInstall ? "Installed" : "Updated"} ${PACKAGE} ${latest}`);
|
|
270
|
+
return {
|
|
271
|
+
skipped: false,
|
|
272
|
+
action: needsInstall ? "installed" : "updated",
|
|
273
|
+
current,
|
|
274
|
+
latest,
|
|
275
|
+
pm
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
148
279
|
// commands/register.ts
|
|
149
280
|
function isCommandAvailable(cmd) {
|
|
150
281
|
try {
|
|
@@ -168,7 +299,7 @@ function detectRuntimes() {
|
|
|
168
299
|
return found;
|
|
169
300
|
}
|
|
170
301
|
function registerCommand() {
|
|
171
|
-
const cmd = new Command("register").description("Register CLI with your Alook account").requiredOption("--token <token>", "API token (starts with al_)").option("--server <url>", "Server URL").option("--profile <name>", "Profile name").action(async (opts, command) => {
|
|
302
|
+
const cmd = new Command("register").description("Register CLI with your Alook account").requiredOption("--token <token>", "API token (starts with al_)").option("--server <url>", "Server URL").option("--profile <name>", "Profile name").option("--no-install", "Skip auto-install/update of @alook/cli").action(async (opts, command) => {
|
|
172
303
|
const token = opts.token;
|
|
173
304
|
const profile = opts.profile || command.parent?.opts().profile;
|
|
174
305
|
const serverUrl = opts.server || command.parent?.opts().server || process.env.ALOOK_SERVER_URL || "https://alook.ai";
|
|
@@ -249,6 +380,7 @@ Usage: ${cmdPrefix()} register --token <token>`);
|
|
|
249
380
|
Registered as ${me.email}`);
|
|
250
381
|
console.log(`Workspace: ${ws.name} (${ws.id})`);
|
|
251
382
|
console.log(`Runtimes: ${activateResp.runtimes.map((r) => r.provider).join(", ")}`);
|
|
383
|
+
await ensureInstalled({ skip: opts.install === false });
|
|
252
384
|
console.log();
|
|
253
385
|
console.log(`Run '${cmdPrefix()} daemon start --foreground' to start the daemon.`);
|
|
254
386
|
});
|
|
@@ -278,7 +410,7 @@ function statusCommand() {
|
|
|
278
410
|
import { Command as Command3 } from "commander";
|
|
279
411
|
import { spawn as spawn2 } from "child_process";
|
|
280
412
|
import { openSync, closeSync, mkdirSync as mkdirSync3 } from "fs";
|
|
281
|
-
import { dirname as
|
|
413
|
+
import { dirname as dirname4 } from "path";
|
|
282
414
|
|
|
283
415
|
// ../shared/src/constants.ts
|
|
284
416
|
var POLL_INTERVAL_MS = Number(process.env.POLL_INTERVAL_MS) || 3000;
|
|
@@ -14518,7 +14650,7 @@ function sql(strings, ...params) {
|
|
|
14518
14650
|
return new SQL([new StringChunk(str)]);
|
|
14519
14651
|
}
|
|
14520
14652
|
sql2.raw = raw;
|
|
14521
|
-
function
|
|
14653
|
+
function join3(chunks, separator) {
|
|
14522
14654
|
const result = [];
|
|
14523
14655
|
for (const [i, chunk] of chunks.entries()) {
|
|
14524
14656
|
if (i > 0 && separator !== undefined) {
|
|
@@ -14528,7 +14660,7 @@ function sql(strings, ...params) {
|
|
|
14528
14660
|
}
|
|
14529
14661
|
return new SQL(result);
|
|
14530
14662
|
}
|
|
14531
|
-
sql2.join =
|
|
14663
|
+
sql2.join = join3;
|
|
14532
14664
|
function identifier(value) {
|
|
14533
14665
|
return new Name(value);
|
|
14534
14666
|
}
|
|
@@ -15581,19 +15713,19 @@ class DaemonClient {
|
|
|
15581
15713
|
|
|
15582
15714
|
// daemon/config.ts
|
|
15583
15715
|
import { hostname as hostname4 } from "os";
|
|
15584
|
-
import { join as
|
|
15716
|
+
import { join as join3 } from "path";
|
|
15585
15717
|
function pidFilePath(profile) {
|
|
15586
15718
|
const name = profile ? `daemon_${profile}.pid` : "daemon.pid";
|
|
15587
|
-
return
|
|
15719
|
+
return join3(configDir(), name);
|
|
15588
15720
|
}
|
|
15589
15721
|
function daemonLogDir() {
|
|
15590
|
-
return
|
|
15722
|
+
return join3(configDir(), "daemon", "logs");
|
|
15591
15723
|
}
|
|
15592
15724
|
function daemonLogFilePath(date5 = new Date) {
|
|
15593
15725
|
const y = date5.getFullYear();
|
|
15594
15726
|
const m = String(date5.getMonth() + 1).padStart(2, "0");
|
|
15595
15727
|
const d = String(date5.getDate()).padStart(2, "0");
|
|
15596
|
-
return
|
|
15728
|
+
return join3(daemonLogDir(), `${y}-${m}-${d}.log`);
|
|
15597
15729
|
}
|
|
15598
15730
|
function parseDuration(s) {
|
|
15599
15731
|
if (!s)
|
|
@@ -15633,7 +15765,7 @@ function loadDaemonConfig(profile) {
|
|
|
15633
15765
|
if (profile && !daemonId.endsWith(`-${profile}`)) {
|
|
15634
15766
|
daemonId = `${daemonId}-${profile}`;
|
|
15635
15767
|
}
|
|
15636
|
-
const defaultRoot =
|
|
15768
|
+
const defaultRoot = join3(configDir(), profile ? `workspaces_${profile}` : "workspaces");
|
|
15637
15769
|
const workspacesRoot = process.env.ALOOK_WORKSPACES_ROOT || defaultRoot;
|
|
15638
15770
|
return {
|
|
15639
15771
|
serverURL: normalizeServerBaseURL(process.env.ALOOK_SERVER_URL || "https://alook.ai"),
|
|
@@ -15815,8 +15947,8 @@ function createLogger2(level) {
|
|
|
15815
15947
|
var log = createLogger2();
|
|
15816
15948
|
|
|
15817
15949
|
// daemon/pidfile.ts
|
|
15818
|
-
import { readFileSync as
|
|
15819
|
-
import { dirname } from "path";
|
|
15950
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync, mkdirSync as mkdirSync2 } from "fs";
|
|
15951
|
+
import { dirname as dirname2 } from "path";
|
|
15820
15952
|
function isProcessAlive(pid) {
|
|
15821
15953
|
try {
|
|
15822
15954
|
process.kill(pid, 0);
|
|
@@ -15827,7 +15959,7 @@ function isProcessAlive(pid) {
|
|
|
15827
15959
|
}
|
|
15828
15960
|
function readDaemonPid(profile) {
|
|
15829
15961
|
try {
|
|
15830
|
-
const content =
|
|
15962
|
+
const content = readFileSync3(pidFilePath(profile), "utf-8").trim();
|
|
15831
15963
|
const pid = parseInt(content, 10);
|
|
15832
15964
|
return Number.isNaN(pid) ? null : pid;
|
|
15833
15965
|
} catch {
|
|
@@ -15837,14 +15969,14 @@ function readDaemonPid(profile) {
|
|
|
15837
15969
|
function acquireDaemonPid(profile) {
|
|
15838
15970
|
const pidPath = pidFilePath(profile);
|
|
15839
15971
|
try {
|
|
15840
|
-
const content =
|
|
15972
|
+
const content = readFileSync3(pidPath, "utf-8").trim();
|
|
15841
15973
|
const existingPid = parseInt(content, 10);
|
|
15842
15974
|
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
15843
15975
|
log.error(`Another daemon is already running (PID ${existingPid}). ` + `Remove ${pidPath} if this is stale.`);
|
|
15844
15976
|
return false;
|
|
15845
15977
|
}
|
|
15846
15978
|
} catch {}
|
|
15847
|
-
mkdirSync2(
|
|
15979
|
+
mkdirSync2(dirname2(pidPath), { recursive: true, mode: 448 });
|
|
15848
15980
|
writeFileSync2(pidPath, String(process.pid), { mode: 384 });
|
|
15849
15981
|
return true;
|
|
15850
15982
|
}
|
|
@@ -15863,8 +15995,8 @@ function releaseDaemonPid(profile) {
|
|
|
15863
15995
|
|
|
15864
15996
|
// daemon/daemon.ts
|
|
15865
15997
|
import { execSync as execSync3, spawn } from "child_process";
|
|
15866
|
-
import { fileURLToPath } from "url";
|
|
15867
|
-
import { dirname as
|
|
15998
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
15999
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
15868
16000
|
function isCommandAvailable2(cmd) {
|
|
15869
16001
|
try {
|
|
15870
16002
|
execSync3(`which ${cmd}`, { stdio: "ignore" });
|
|
@@ -16036,7 +16168,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16036
16168
|
process.on("SIGINT", shutdown);
|
|
16037
16169
|
await pollCycle();
|
|
16038
16170
|
}
|
|
16039
|
-
var SESSION_RUNNER_PATH =
|
|
16171
|
+
var SESSION_RUNNER_PATH = join4(dirname3(fileURLToPath2(import.meta.url)), "session-runner.ts");
|
|
16040
16172
|
function spawnSessionRunner(input) {
|
|
16041
16173
|
const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
|
|
16042
16174
|
const child = spawn("bun", ["run", SESSION_RUNNER_PATH, encoded], {
|
|
@@ -16114,7 +16246,7 @@ async function startInBackground(profile, serverUrl) {
|
|
|
16114
16246
|
return;
|
|
16115
16247
|
}
|
|
16116
16248
|
const logPath = daemonLogFilePath();
|
|
16117
|
-
mkdirSync3(
|
|
16249
|
+
mkdirSync3(dirname4(logPath), { recursive: true, mode: 448 });
|
|
16118
16250
|
const logFd = openSync(logPath, "a", 384);
|
|
16119
16251
|
const child = spawn2(process.execPath, buildChildArgs(profile, serverUrl), {
|
|
16120
16252
|
detached: true,
|
|
@@ -16225,7 +16357,7 @@ function configCommand() {
|
|
|
16225
16357
|
// commands/email.ts
|
|
16226
16358
|
import { Command as Command5 } from "commander";
|
|
16227
16359
|
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
|
|
16228
|
-
import { join as
|
|
16360
|
+
import { join as join5 } from "path";
|
|
16229
16361
|
import PostalMime from "postal-mime";
|
|
16230
16362
|
var VALID_STATUSES = ["unread", "read", "archived"];
|
|
16231
16363
|
var EMAIL_DIR = "/tmp/alook-emails";
|
|
@@ -16275,7 +16407,7 @@ function emailCommand() {
|
|
|
16275
16407
|
mkdirSync4(EMAIL_DIR, { recursive: true });
|
|
16276
16408
|
const downloadedPaths = [];
|
|
16277
16409
|
for (const email3 of emails2) {
|
|
16278
|
-
const emailDir =
|
|
16410
|
+
const emailDir = join5(EMAIL_DIR, email3.id);
|
|
16279
16411
|
mkdirSync4(emailDir, { recursive: true });
|
|
16280
16412
|
const metadata = {
|
|
16281
16413
|
id: email3.id,
|
|
@@ -16285,7 +16417,7 @@ function emailCommand() {
|
|
|
16285
16417
|
date: email3.created_at,
|
|
16286
16418
|
status: email3.status
|
|
16287
16419
|
};
|
|
16288
|
-
const metadataPath =
|
|
16420
|
+
const metadataPath = join5(emailDir, "metadata.json");
|
|
16289
16421
|
writeFileSync3(metadataPath, JSON.stringify(metadata, null, 2));
|
|
16290
16422
|
downloadedPaths.push(metadataPath);
|
|
16291
16423
|
let rawMime;
|
|
@@ -16301,17 +16433,17 @@ function emailCommand() {
|
|
|
16301
16433
|
}
|
|
16302
16434
|
const parsed = await new PostalMime().parse(rawMime);
|
|
16303
16435
|
if (parsed.text) {
|
|
16304
|
-
const bodyPath =
|
|
16436
|
+
const bodyPath = join5(emailDir, "body.txt");
|
|
16305
16437
|
writeFileSync3(bodyPath, parsed.text);
|
|
16306
16438
|
downloadedPaths.push(bodyPath);
|
|
16307
16439
|
}
|
|
16308
16440
|
if (parsed.html) {
|
|
16309
|
-
const htmlPath =
|
|
16441
|
+
const htmlPath = join5(emailDir, "body.html");
|
|
16310
16442
|
writeFileSync3(htmlPath, parsed.html);
|
|
16311
16443
|
downloadedPaths.push(htmlPath);
|
|
16312
16444
|
}
|
|
16313
16445
|
if (parsed.attachments && parsed.attachments.length > 0) {
|
|
16314
|
-
const attDir =
|
|
16446
|
+
const attDir = join5(emailDir, "attachments");
|
|
16315
16447
|
mkdirSync4(attDir, { recursive: true });
|
|
16316
16448
|
const usedFilenames = new Set;
|
|
16317
16449
|
for (let i = 0;i < parsed.attachments.length; i++) {
|
|
@@ -16321,7 +16453,7 @@ function emailCommand() {
|
|
|
16321
16453
|
filename = `${i}-${filename}`;
|
|
16322
16454
|
}
|
|
16323
16455
|
usedFilenames.add(filename);
|
|
16324
|
-
const attPath =
|
|
16456
|
+
const attPath = join5(attDir, filename);
|
|
16325
16457
|
const content = att.content;
|
|
16326
16458
|
let buf;
|
|
16327
16459
|
if (typeof content === "string") {
|
|
@@ -16367,25 +16499,9 @@ function emailCommand() {
|
|
|
16367
16499
|
|
|
16368
16500
|
// commands/version.ts
|
|
16369
16501
|
import { Command as Command6 } from "commander";
|
|
16370
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
16371
|
-
import { join as join5, dirname as dirname4 } from "path";
|
|
16372
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16373
16502
|
function versionCommand() {
|
|
16374
16503
|
const cmd = new Command6("version").description("Show CLI version").action(() => {
|
|
16375
|
-
|
|
16376
|
-
let version3 = "unknown";
|
|
16377
|
-
try {
|
|
16378
|
-
const pkgPath = join5(__dirname2, "..", "package.json");
|
|
16379
|
-
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
16380
|
-
version3 = pkg.version;
|
|
16381
|
-
} catch {
|
|
16382
|
-
try {
|
|
16383
|
-
const pkgPath = join5(__dirname2, "..", "..", "package.json");
|
|
16384
|
-
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
16385
|
-
version3 = pkg.version;
|
|
16386
|
-
} catch {}
|
|
16387
|
-
}
|
|
16388
|
-
console.log(`alook version ${version3}`);
|
|
16504
|
+
console.log(`alook version ${getCurrentVersion()}`);
|
|
16389
16505
|
});
|
|
16390
16506
|
return cmd;
|
|
16391
16507
|
}
|