@nanopm/cli 0.1.0 → 0.1.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/dist/index.js +82 -71
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -606,6 +606,13 @@ function hashDeviceSecret(secret) {
|
|
|
606
606
|
return createHash("sha256").update(secret).digest("hex");
|
|
607
607
|
}
|
|
608
608
|
|
|
609
|
+
// ../shared/src/harness.ts
|
|
610
|
+
var CLAUDE_CODE = "claude-code";
|
|
611
|
+
function claudeHarness(harnesses) {
|
|
612
|
+
const entries = Array.isArray(harnesses) ? harnesses : [];
|
|
613
|
+
return entries.find((h) => h.id === CLAUDE_CODE) ?? null;
|
|
614
|
+
}
|
|
615
|
+
|
|
609
616
|
// ../shared/src/secrets.ts
|
|
610
617
|
var SECRET_PATTERNS = [
|
|
611
618
|
"**/.env",
|
|
@@ -10829,7 +10836,7 @@ async function runDaemon(options = {}) {
|
|
|
10829
10836
|
let daemonId;
|
|
10830
10837
|
try {
|
|
10831
10838
|
auth = await authenticate();
|
|
10832
|
-
daemonId = await registerDaemon(auth, options.version ?? "dev", options.shared);
|
|
10839
|
+
daemonId = await registerDaemon(auth, options.version ?? "dev", { shared: options.shared });
|
|
10833
10840
|
log(`daemon ${daemonId} online as ${auth.userId} on ${hostname2()}`);
|
|
10834
10841
|
for (const reclaimed of await reclaimOwnJobs(auth.client, daemonId)) {
|
|
10835
10842
|
log(`job ${reclaimed} was running when this daemon last stopped; marked interrupted (resume with \`nanopm resume ${reclaimed}\`)`);
|
|
@@ -11137,7 +11144,7 @@ async function killOrphanedHarness(jobId) {
|
|
|
11137
11144
|
}
|
|
11138
11145
|
await rm3(runDir, { recursive: true, force: true });
|
|
11139
11146
|
}
|
|
11140
|
-
async function registerDaemon(auth, version,
|
|
11147
|
+
async function registerDaemon(auth, version, opts = {}) {
|
|
11141
11148
|
const path = join10(nanopmHome(), "daemon.json");
|
|
11142
11149
|
let identity = null;
|
|
11143
11150
|
try {
|
|
@@ -11145,18 +11152,18 @@ async function registerDaemon(auth, version, shared) {
|
|
|
11145
11152
|
} catch {
|
|
11146
11153
|
identity = null;
|
|
11147
11154
|
}
|
|
11148
|
-
const isShared = shared ?? identity?.shared ?? false;
|
|
11149
|
-
const detection = await new ClaudeCodeAdapter().detect();
|
|
11150
|
-
if (detection.authenticated === null && identity?.id) {
|
|
11155
|
+
const isShared = opts.shared ?? identity?.shared ?? false;
|
|
11156
|
+
const detection = opts.claude ?? await new ClaudeCodeAdapter().detect();
|
|
11157
|
+
if (!opts.claude && detection.authenticated === null && identity?.id) {
|
|
11151
11158
|
const { data: existing2 } = await auth.client.from("daemons").select("harnesses").eq("id", identity.id).maybeSingle();
|
|
11152
|
-
const known = existing2?.harnesses
|
|
11159
|
+
const known = claudeHarness(existing2?.harnesses);
|
|
11153
11160
|
if (typeof known?.authenticated === "boolean") detection.authenticated = known.authenticated;
|
|
11154
11161
|
}
|
|
11155
11162
|
const fields = {
|
|
11156
11163
|
hostname: hostname2(),
|
|
11157
11164
|
version,
|
|
11158
|
-
harnesses: [{ id:
|
|
11159
|
-
status: "online",
|
|
11165
|
+
harnesses: [{ id: CLAUDE_CODE, ...detection }],
|
|
11166
|
+
status: opts.claude ? "offline" : "online",
|
|
11160
11167
|
shared: isShared,
|
|
11161
11168
|
last_seen_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
11162
11169
|
};
|
|
@@ -13874,6 +13881,59 @@ import { mkdir as mkdir6, readFile as readFile11, rm as rm4, writeFile as writeF
|
|
|
13874
13881
|
import { homedir as homedir3 } from "os";
|
|
13875
13882
|
import { join as join12 } from "path";
|
|
13876
13883
|
import { promisify as promisify7 } from "util";
|
|
13884
|
+
|
|
13885
|
+
// package.json
|
|
13886
|
+
var package_default = {
|
|
13887
|
+
name: "@nanopm/cli",
|
|
13888
|
+
version: "0.1.1",
|
|
13889
|
+
description: "An autonomous product manager for builders who run several small products. Runs on your machine, drives your own coding harness.",
|
|
13890
|
+
type: "module",
|
|
13891
|
+
license: "MIT",
|
|
13892
|
+
bin: {
|
|
13893
|
+
nanopm: "dist/index.js"
|
|
13894
|
+
},
|
|
13895
|
+
files: [
|
|
13896
|
+
"dist",
|
|
13897
|
+
"skills"
|
|
13898
|
+
],
|
|
13899
|
+
engines: {
|
|
13900
|
+
node: ">=20"
|
|
13901
|
+
},
|
|
13902
|
+
scripts: {
|
|
13903
|
+
build: "tsup && rm -rf skills && cp -R ../../skills skills"
|
|
13904
|
+
},
|
|
13905
|
+
dependencies: {
|
|
13906
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
13907
|
+
"@supabase/supabase-js": "^2.116.0",
|
|
13908
|
+
commander: "^15.0.0",
|
|
13909
|
+
postgres: "^3.4.7",
|
|
13910
|
+
zod: "^4.5.4"
|
|
13911
|
+
},
|
|
13912
|
+
repository: {
|
|
13913
|
+
type: "git",
|
|
13914
|
+
url: "git+https://github.com/nmrtn/nanopm2.git"
|
|
13915
|
+
},
|
|
13916
|
+
keywords: [
|
|
13917
|
+
"product-management",
|
|
13918
|
+
"claude-code",
|
|
13919
|
+
"agent",
|
|
13920
|
+
"solopreneur"
|
|
13921
|
+
],
|
|
13922
|
+
devDependencies: {
|
|
13923
|
+
"@nanopm/connectors": "workspace:*",
|
|
13924
|
+
"@nanopm/daemon": "workspace:*",
|
|
13925
|
+
"@nanopm/memory-mcp": "workspace:*",
|
|
13926
|
+
"@nanopm/shared": "workspace:*"
|
|
13927
|
+
},
|
|
13928
|
+
publishConfig: {
|
|
13929
|
+
access: "public"
|
|
13930
|
+
}
|
|
13931
|
+
};
|
|
13932
|
+
|
|
13933
|
+
// src/version.ts
|
|
13934
|
+
var VERSION = package_default.version;
|
|
13935
|
+
|
|
13936
|
+
// src/commands/daemon-service.ts
|
|
13877
13937
|
var execFileAsync7 = promisify7(execFile7);
|
|
13878
13938
|
var LAUNCHD_LABEL = "pm.nano.daemon";
|
|
13879
13939
|
function plistPath() {
|
|
@@ -13919,12 +13979,20 @@ function requireMac() {
|
|
|
13919
13979
|
async function installDaemon(out = (l) => console.log(l), opts = {}) {
|
|
13920
13980
|
requireMac();
|
|
13921
13981
|
if (!await loadCredentials()) throw new Error("not logged in; run `nanopm login` first");
|
|
13982
|
+
const auth = await authenticate();
|
|
13983
|
+
const found = (claude) => registerDaemon(auth, VERSION, { shared: opts.shared, claude }).catch(() => void 0);
|
|
13922
13984
|
const adapter = new ClaudeCodeAdapter();
|
|
13923
13985
|
const detection = await adapter.detect();
|
|
13924
|
-
if (!detection.available)
|
|
13986
|
+
if (!detection.available) {
|
|
13987
|
+
await found(detection);
|
|
13988
|
+
throw new Error("Claude Code is not installed on this machine (`claude` not found on PATH)");
|
|
13989
|
+
}
|
|
13925
13990
|
out(`Claude Code ${detection.version} found; checking its login\u2026`);
|
|
13926
13991
|
const ping = await adapter.ping();
|
|
13927
|
-
if (!ping.ok)
|
|
13992
|
+
if (!ping.ok) {
|
|
13993
|
+
await found({ ...detection, authenticated: false });
|
|
13994
|
+
throw new Error(`Claude Code is installed but not usable headless: ${ping.message}`);
|
|
13995
|
+
}
|
|
13928
13996
|
out("Claude Code answers.");
|
|
13929
13997
|
const web = await adapter.probeWebSearch();
|
|
13930
13998
|
await rememberWebSearch(web.ok);
|
|
@@ -13936,6 +14004,7 @@ async function installDaemon(out = (l) => console.log(l), opts = {}) {
|
|
|
13936
14004
|
await mkdir6(join12(home, "Library", "LaunchAgents"), { recursive: true });
|
|
13937
14005
|
await mkdir6(nanopmHome(), { recursive: true, mode: 448 });
|
|
13938
14006
|
if (opts.shared !== void 0) await rememberShared(opts.shared);
|
|
14007
|
+
await found({ ...detection, authenticated: true });
|
|
13939
14008
|
await writeFile4(plistPath(), renderPlist({ node: process.execPath, bin, home, path, logPath: daemonLogPath(), nanopmHome: nanopmHome() }), { mode: 420 });
|
|
13940
14009
|
const target = `gui/${process.getuid?.() ?? 501}`;
|
|
13941
14010
|
await execFileAsync7("launchctl", ["bootout", target, plistPath()]).catch(() => void 0);
|
|
@@ -13944,15 +14013,6 @@ async function installDaemon(out = (l) => console.log(l), opts = {}) {
|
|
|
13944
14013
|
const shared = opts.shared ?? await localShared();
|
|
13945
14014
|
out(shared ? "This machine is shared with your projects: it also runs scheduled reviews and partners' jobs." : "This machine runs your jobs only. `nanopm daemon install --share` to offer it to your projects.");
|
|
13946
14015
|
if (opts.next !== false) out(`Next, in a repo: nanopm onboard \xB7 or continue at ${webUrl()}/new`);
|
|
13947
|
-
const auth = await authenticate();
|
|
13948
|
-
for (let i = 0; i < 20; i++) {
|
|
13949
|
-
const id = await localDaemonId();
|
|
13950
|
-
if (id) {
|
|
13951
|
-
await auth.client.from("daemons").update({ harnesses: [{ id: "claude-code", available: true, version: detection.version, authenticated: true }] }).eq("id", id);
|
|
13952
|
-
break;
|
|
13953
|
-
}
|
|
13954
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
13955
|
-
}
|
|
13956
14016
|
}
|
|
13957
14017
|
async function uninstallDaemon(out = (l) => console.log(l)) {
|
|
13958
14018
|
requireMac();
|
|
@@ -14222,7 +14282,9 @@ async function login(opts = {}) {
|
|
|
14222
14282
|
await installDaemon(out, { next: false });
|
|
14223
14283
|
} catch (err) {
|
|
14224
14284
|
out(`could not start me in the background: ${err instanceof Error ? err.message : String(err)}`);
|
|
14225
|
-
out("
|
|
14285
|
+
out("");
|
|
14286
|
+
out("Your account is linked, but I cannot work on this machine yet. Fix the line above, then run: nanopm daemon install. The page in your browser moves on by itself.");
|
|
14287
|
+
return;
|
|
14226
14288
|
}
|
|
14227
14289
|
}
|
|
14228
14290
|
out("");
|
|
@@ -15523,57 +15585,6 @@ async function resume(jobId, opts) {
|
|
|
15523
15585
|
return streamJob(auth.client, job.id, { pollMs: 1e3, out, renderer, cancelSignal: opts.cancelSignal, answerer, verbose: verboseWanted() });
|
|
15524
15586
|
}
|
|
15525
15587
|
|
|
15526
|
-
// package.json
|
|
15527
|
-
var package_default = {
|
|
15528
|
-
name: "@nanopm/cli",
|
|
15529
|
-
version: "0.1.0",
|
|
15530
|
-
description: "An autonomous product manager for builders who run several small products. Runs on your machine, drives your own coding harness.",
|
|
15531
|
-
type: "module",
|
|
15532
|
-
license: "MIT",
|
|
15533
|
-
bin: {
|
|
15534
|
-
nanopm: "dist/index.js"
|
|
15535
|
-
},
|
|
15536
|
-
files: [
|
|
15537
|
-
"dist",
|
|
15538
|
-
"skills"
|
|
15539
|
-
],
|
|
15540
|
-
engines: {
|
|
15541
|
-
node: ">=20"
|
|
15542
|
-
},
|
|
15543
|
-
scripts: {
|
|
15544
|
-
build: "tsup && rm -rf skills && cp -R ../../skills skills"
|
|
15545
|
-
},
|
|
15546
|
-
dependencies: {
|
|
15547
|
-
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
15548
|
-
"@supabase/supabase-js": "^2.116.0",
|
|
15549
|
-
commander: "^15.0.0",
|
|
15550
|
-
postgres: "^3.4.7",
|
|
15551
|
-
zod: "^4.5.4"
|
|
15552
|
-
},
|
|
15553
|
-
repository: {
|
|
15554
|
-
type: "git",
|
|
15555
|
-
url: "git+https://github.com/nmrtn/nanopm2.git"
|
|
15556
|
-
},
|
|
15557
|
-
keywords: [
|
|
15558
|
-
"product-management",
|
|
15559
|
-
"claude-code",
|
|
15560
|
-
"agent",
|
|
15561
|
-
"solopreneur"
|
|
15562
|
-
],
|
|
15563
|
-
devDependencies: {
|
|
15564
|
-
"@nanopm/connectors": "workspace:*",
|
|
15565
|
-
"@nanopm/daemon": "workspace:*",
|
|
15566
|
-
"@nanopm/memory-mcp": "workspace:*",
|
|
15567
|
-
"@nanopm/shared": "workspace:*"
|
|
15568
|
-
},
|
|
15569
|
-
publishConfig: {
|
|
15570
|
-
access: "public"
|
|
15571
|
-
}
|
|
15572
|
-
};
|
|
15573
|
-
|
|
15574
|
-
// src/version.ts
|
|
15575
|
-
var VERSION = package_default.version;
|
|
15576
|
-
|
|
15577
15588
|
// src/commands/connect.ts
|
|
15578
15589
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
15579
15590
|
function whereItGoes(color) {
|
package/package.json
CHANGED