@promptster/teams-cli 0.6.1 → 0.8.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/bin/promptster-teams.js +55 -20
- package/lib/resolve.js +166 -0
- package/package.json +12 -2
- package/scripts/postinstall.js +202 -0
- package/binaries/SHA256SUMS +0 -6
- package/binaries/SHA256SUMS.minisig +0 -4
- package/binaries/promptster-teams-darwin-arm64 +0 -0
- package/binaries/promptster-teams-darwin-x64 +0 -0
- package/binaries/promptster-teams-linux-arm64 +0 -0
- package/binaries/promptster-teams-linux-x64 +0 -0
- package/binaries/promptster-teams-win32-arm64.exe +0 -0
- package/binaries/promptster-teams-win32-x64.exe +0 -0
package/bin/promptster-teams.js
CHANGED
|
@@ -1,31 +1,66 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
-
//
|
|
4
|
+
// Launcher: execs the MANAGED binary (~/.promptster-teams/bin), falling back to
|
|
5
|
+
// the copy bundled in node_modules. See lib/resolve.js for why the binary that
|
|
6
|
+
// runs must not be the one npm is tracking.
|
|
7
|
+
//
|
|
8
|
+
// Preferring `managed` is what keeps npm honest: self-update rewrites the
|
|
9
|
+
// managed file, node_modules is never touched, so `npm ls` keeps telling the
|
|
10
|
+
// truth about what npm installed.
|
|
11
|
+
|
|
5
12
|
const { spawnSync } = require("child_process");
|
|
6
|
-
const path = require("path");
|
|
7
13
|
const fs = require("fs");
|
|
8
14
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
const {
|
|
16
|
+
PLATFORMS,
|
|
17
|
+
bundledBinPath,
|
|
18
|
+
managedBinPath,
|
|
19
|
+
isGlobalInstall,
|
|
20
|
+
platformKey,
|
|
21
|
+
platformPackage,
|
|
22
|
+
} = require("../lib/resolve");
|
|
23
|
+
|
|
24
|
+
function usable(p) {
|
|
25
|
+
if (!p) return false;
|
|
26
|
+
try {
|
|
27
|
+
fs.accessSync(p, fs.constants.X_OK);
|
|
28
|
+
return true;
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
23
32
|
}
|
|
24
33
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
const bundled = bundledBinPath();
|
|
35
|
+
|
|
36
|
+
// A project-local install runs ITS OWN binary, never the shared managed one.
|
|
37
|
+
// The managed binary is per-user; a lockfile is a per-project pin. Pointing a
|
|
38
|
+
// local install at the shared file would mean a repo pinning 0.5.0 executes
|
|
39
|
+
// whatever another repo last installed — the lockfile would select nothing.
|
|
40
|
+
//
|
|
41
|
+
// Otherwise: managed first. If postinstall could not write it (--ignore-scripts,
|
|
42
|
+
// read-only home, unresolvable HOME), fall back to bundled so the CLI still runs
|
|
43
|
+
// — it just self-updates inside node_modules the way it used to, and npm ls goes
|
|
44
|
+
// back to drifting. Working-but-drifting beats not working.
|
|
45
|
+
const managed = isGlobalInstall() ? managedBinPath() : null;
|
|
46
|
+
const binPath = usable(managed) ? managed : bundled;
|
|
47
|
+
|
|
48
|
+
if (!usable(binPath)) {
|
|
49
|
+
// The binary now arrives as a per-platform optionalDependency, and npm treats
|
|
50
|
+
// a missing optional dep as a SUCCESSFUL install — no error, no warning. So
|
|
51
|
+
// this is the first and only place an engineer learns anything is wrong, and
|
|
52
|
+
// a bare "binary not found" would send them hunting the wrong thing. Name the
|
|
53
|
+
// package that is actually missing and the most likely cause.
|
|
54
|
+
if (!PLATFORMS.includes(platformKey())) {
|
|
55
|
+
console.error(`promptster-teams: unsupported platform ${platformKey()}`);
|
|
56
|
+
console.error(`Supported: ${PLATFORMS.join(", ")}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
console.error(
|
|
60
|
+
`promptster-teams: no binary found — ${platformPackage()} is not installed.`
|
|
61
|
+
);
|
|
62
|
+
console.error("This usually means the install used --omit=optional/--no-optional.");
|
|
63
|
+
console.error("Fix: npm i -g @promptster/teams-cli (without omitting optional deps)");
|
|
29
64
|
process.exit(1);
|
|
30
65
|
}
|
|
31
66
|
|
package/lib/resolve.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Shared path/platform resolution for the npm package.
|
|
4
|
+
//
|
|
5
|
+
// WHY THIS PACKAGE IS A LAUNCHER, NOT THE INSTALL
|
|
6
|
+
// ------------------------------------------------
|
|
7
|
+
// The Go binary self-updates by renaming a verified new build over its own
|
|
8
|
+
// executable. When the executable it swaps lives inside node_modules, npm's
|
|
9
|
+
// metadata immediately starts lying: `npm ls` / `npm outdated` keep reporting
|
|
10
|
+
// the version npm installed while the binary underneath has moved on, and a
|
|
11
|
+
// reinstall silently writes the older binary back.
|
|
12
|
+
//
|
|
13
|
+
// You cannot fix that by rewriting package.json behind npm's back — that is a
|
|
14
|
+
// hack no mature CLI does, and it does not even work for project-local installs
|
|
15
|
+
// (npm ls reads the lockfile, not the installed package.json — verified).
|
|
16
|
+
//
|
|
17
|
+
// So the binary does not live in node_modules. postinstall.js copies it to the
|
|
18
|
+
// MANAGED path (~/.promptster-teams/bin — the same place install.sh writes),
|
|
19
|
+
// and bin/promptster-teams.js execs that. node_modules keeps a pristine copy
|
|
20
|
+
// that nothing ever mutates, so npm's metadata stays true by construction, and
|
|
21
|
+
// self-update stays fully automatic — which matters because this is a daemon:
|
|
22
|
+
// unlike claude/codex, there is no human reading a "please upgrade" nudge.
|
|
23
|
+
//
|
|
24
|
+
// That pristine copy arrives via a per-platform optionalDependency gated on
|
|
25
|
+
// npm's os/cpu fields, so an install downloads ONE binary (~18MB) instead of the
|
|
26
|
+
// six (~74.5MB) that used to ship in a single tarball. The tradeoff: optional
|
|
27
|
+
// deps fail SILENTLY, so every consumer of bundledBinPath() must handle null.
|
|
28
|
+
//
|
|
29
|
+
// Keep this file dependency-free: postinstall must run before anything is
|
|
30
|
+
// installed, and the launcher is on the hot path of every invocation.
|
|
31
|
+
|
|
32
|
+
const fs = require("fs");
|
|
33
|
+
const os = require("os");
|
|
34
|
+
const path = require("path");
|
|
35
|
+
|
|
36
|
+
// The platform-arch keys we publish a binary package for. The package name is
|
|
37
|
+
// always `<wrapper>-<key>` and the binary inside is always named canonically,
|
|
38
|
+
// so the key is the only thing that varies.
|
|
39
|
+
const PLATFORMS = [
|
|
40
|
+
"darwin-x64",
|
|
41
|
+
"darwin-arm64",
|
|
42
|
+
"linux-x64",
|
|
43
|
+
"linux-arm64",
|
|
44
|
+
"win32-x64",
|
|
45
|
+
"win32-arm64",
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const PKG_PREFIX = "@promptster/teams-cli";
|
|
49
|
+
|
|
50
|
+
function platformKey() {
|
|
51
|
+
return `${process.platform}-${process.arch}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function platformPackage(key) {
|
|
55
|
+
return `${PKG_PREFIX}-${key || platformKey()}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// bundledBinPath is the pristine copy npm installed — now inside the per-platform
|
|
59
|
+
// optionalDependency (@promptster/teams-cli-darwin-arm64, …) rather than a
|
|
60
|
+
// binaries/ directory carrying all six. npm skips the packages whose os/cpu do
|
|
61
|
+
// not match the host, so exactly one is ever present.
|
|
62
|
+
//
|
|
63
|
+
// Returns null when the platform package is absent. That is a REAL state, not a
|
|
64
|
+
// theoretical one: optional dependencies fail SILENTLY by design, so
|
|
65
|
+
// --omit=optional, a partial registry publish, or an unsupported platform all
|
|
66
|
+
// land here with npm reporting success. Callers must degrade, never assume.
|
|
67
|
+
//
|
|
68
|
+
// Resolved via require.resolve rather than a hardcoded ../../ path so it works
|
|
69
|
+
// wherever the package manager actually put it — hoisted to a root
|
|
70
|
+
// node_modules, nested, or in a pnpm store.
|
|
71
|
+
function bundledBinPath() {
|
|
72
|
+
const key = platformKey();
|
|
73
|
+
if (!PLATFORMS.includes(key)) return null;
|
|
74
|
+
const binName = key.startsWith("win32-")
|
|
75
|
+
? "promptster-teams.exe"
|
|
76
|
+
: "promptster-teams";
|
|
77
|
+
try {
|
|
78
|
+
const pkgJson = require.resolve(`${platformPackage(key)}/package.json`);
|
|
79
|
+
const p = path.join(path.dirname(pkgJson), binName);
|
|
80
|
+
return fs.existsSync(p) ? p : null;
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// managedBinPath MUST stay in lockstep with Go's state.CanonicalInstallBin and
|
|
87
|
+
// install.sh's INSTALL_DIR. All three name the same file; if they diverge, an
|
|
88
|
+
// npm install and a curl install manage two different binaries on one box and
|
|
89
|
+
// PATH decides which one the engineer actually runs.
|
|
90
|
+
function managedBinPath() {
|
|
91
|
+
const home = os.homedir();
|
|
92
|
+
if (!home) return null;
|
|
93
|
+
const name =
|
|
94
|
+
process.platform === "win32" ? "promptster-teams.exe" : "promptster-teams";
|
|
95
|
+
return path.join(home, ".promptster-teams", "bin", name);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// isGlobalInstall reports whether this package was installed into a GLOBAL
|
|
99
|
+
// prefix rather than a project's node_modules.
|
|
100
|
+
//
|
|
101
|
+
// This gate is why a project-local install does not touch the managed binary at
|
|
102
|
+
// all. The managed binary is per-USER and shared; a project's lockfile is a
|
|
103
|
+
// deliberate per-PROJECT pin. If a local install pointed at the shared binary,
|
|
104
|
+
// a repo pinning 0.5.0 and one pinning 0.6.1 would both execute whichever
|
|
105
|
+
// version happens to be in ~/.promptster-teams/bin — the lockfile would select
|
|
106
|
+
// nothing at all. That is strictly worse than the npm-ls drift this design
|
|
107
|
+
// removes, so local installs stay entirely inside their own node_modules.
|
|
108
|
+
//
|
|
109
|
+
// Mirrors nudgeFor's layout rules in internal/selfupdate/selfupdate.go: match
|
|
110
|
+
// path SEGMENTS (never substrings — a directory called my-node_modules-backup
|
|
111
|
+
// is not npm), and split on both separators so the check is host-independent.
|
|
112
|
+
function pathSegments(p) {
|
|
113
|
+
return String(p || "").split(/[/\\]/).filter(Boolean);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function isGlobalInstall(selfDir) {
|
|
117
|
+
const segs = pathSegments(selfDir === undefined ? __dirname : selfDir);
|
|
118
|
+
if (!segs.includes("node_modules")) {
|
|
119
|
+
// Not under node_modules at all (e.g. `npm link`, a checkout). Not a
|
|
120
|
+
// project-local install, so it may manage the shared binary.
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
const pnpm = segs.includes(".pnpm") || segs.includes("pnpm");
|
|
124
|
+
const adjacent = (a, b) =>
|
|
125
|
+
segs.some((s, i) => s === a && segs[i + 1] === b);
|
|
126
|
+
|
|
127
|
+
// pnpm's global prefix, e.g. ~/Library/pnpm/global/5/node_modules/...
|
|
128
|
+
if (pnpm && segs.includes("global")) return true;
|
|
129
|
+
// npm's global prefix: <prefix>/lib/node_modules (unix) or
|
|
130
|
+
// <AppData>\npm\node_modules (windows).
|
|
131
|
+
if (!pnpm && (adjacent("lib", "node_modules") || adjacent("npm", "node_modules"))) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// parseVersion turns "1.2.3" into [1,2,3]; returns null for anything that is
|
|
138
|
+
// not a plain 3-part numeric version. Deliberately strict rather than a semver
|
|
139
|
+
// dependency: the release pipeline only ever produces x.y.z, and an unparseable
|
|
140
|
+
// version must fall through to "unknown", never to a wrong comparison.
|
|
141
|
+
function parseVersion(s) {
|
|
142
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(String(s || "").trim());
|
|
143
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// isNewer mirrors the Go updater's strictly-newer gate: a is newer than b.
|
|
147
|
+
function isNewer(a, b) {
|
|
148
|
+
const x = parseVersion(a);
|
|
149
|
+
const y = parseVersion(b);
|
|
150
|
+
if (!x || !y) return false;
|
|
151
|
+
for (let i = 0; i < 3; i++) {
|
|
152
|
+
if (x[i] !== y[i]) return x[i] > y[i];
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = {
|
|
158
|
+
PLATFORMS,
|
|
159
|
+
platformKey,
|
|
160
|
+
platformPackage,
|
|
161
|
+
bundledBinPath,
|
|
162
|
+
managedBinPath,
|
|
163
|
+
isGlobalInstall,
|
|
164
|
+
parseVersion,
|
|
165
|
+
isNewer,
|
|
166
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptster/teams-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "On-device, auditable AI-coding capture for internal engineering teams",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"promptster",
|
|
@@ -21,11 +21,13 @@
|
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "node scripts/build.js",
|
|
24
|
+
"postinstall": "node scripts/postinstall.js",
|
|
24
25
|
"prepublishOnly": "node scripts/build.js && node scripts/check-binaries.js"
|
|
25
26
|
},
|
|
26
27
|
"files": [
|
|
27
28
|
"bin/",
|
|
28
|
-
"
|
|
29
|
+
"lib/",
|
|
30
|
+
"scripts/postinstall.js",
|
|
29
31
|
"README.md"
|
|
30
32
|
],
|
|
31
33
|
"engines": {
|
|
@@ -33,5 +35,13 @@
|
|
|
33
35
|
},
|
|
34
36
|
"publishConfig": {
|
|
35
37
|
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"optionalDependencies": {
|
|
40
|
+
"@promptster/teams-cli-linux-x64": "0.8.0",
|
|
41
|
+
"@promptster/teams-cli-linux-arm64": "0.8.0",
|
|
42
|
+
"@promptster/teams-cli-darwin-x64": "0.8.0",
|
|
43
|
+
"@promptster/teams-cli-darwin-arm64": "0.8.0",
|
|
44
|
+
"@promptster/teams-cli-win32-x64": "0.8.0",
|
|
45
|
+
"@promptster/teams-cli-win32-arm64": "0.8.0"
|
|
36
46
|
}
|
|
37
47
|
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// Installs the platform binary from node_modules to the MANAGED path
|
|
5
|
+
// (~/.promptster-teams/bin/promptster-teams), which is what actually runs and
|
|
6
|
+
// what self-update owns. See lib/resolve.js for why the binary must not run
|
|
7
|
+
// from inside node_modules.
|
|
8
|
+
//
|
|
9
|
+
// Contract: this script must NEVER fail an npm install. A postinstall that
|
|
10
|
+
// exits non-zero aborts `npm i -g` and leaves the engineer with no CLI at all —
|
|
11
|
+
// far worse than the drift it exists to fix. Every failure path warns and exits
|
|
12
|
+
// 0; bin/promptster-teams.js then falls back to the bundled binary, which works
|
|
13
|
+
// exactly as it did before this file existed.
|
|
14
|
+
|
|
15
|
+
const { spawnSync } = require("child_process");
|
|
16
|
+
const fs = require("fs");
|
|
17
|
+
const path = require("path");
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
PLATFORMS,
|
|
21
|
+
bundledBinPath,
|
|
22
|
+
managedBinPath,
|
|
23
|
+
isGlobalInstall,
|
|
24
|
+
isNewer,
|
|
25
|
+
platformKey,
|
|
26
|
+
platformPackage,
|
|
27
|
+
} = require("../lib/resolve");
|
|
28
|
+
|
|
29
|
+
function warn(msg) {
|
|
30
|
+
console.warn(`promptster-teams: ${msg}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// binVersion asks a binary what it is. One that cannot answer (missing,
|
|
34
|
+
// corrupt, wrong arch, not executable) reports null.
|
|
35
|
+
//
|
|
36
|
+
// Both sides of the downgrade guard go through this deliberately. The obvious
|
|
37
|
+
// shortcut is to compare package.json's version against the managed binary —
|
|
38
|
+
// but that trusts package.json to describe the bytes sitting in binaries/, and
|
|
39
|
+
// when those two disagree the guard makes its decision on a fiction (and the
|
|
40
|
+
// success log prints a version that was never installed). The pipeline does gate
|
|
41
|
+
// package.json against the tag, so they agree today; comparing the actual binary
|
|
42
|
+
// to the actual binary means the guard cannot be wrong even if that gate is ever
|
|
43
|
+
// lost. It costs one ~10ms spawn on install.
|
|
44
|
+
function binVersion(bin) {
|
|
45
|
+
if (!bin || !fs.existsSync(bin)) return null;
|
|
46
|
+
try {
|
|
47
|
+
const r = spawnSync(bin, ["--version"], {
|
|
48
|
+
encoding: "utf8",
|
|
49
|
+
timeout: 10_000,
|
|
50
|
+
});
|
|
51
|
+
if (r.status !== 0 || !r.stdout) return null;
|
|
52
|
+
return r.stdout.trim().split("\n")[0].trim() || null;
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// repairAutostart re-points an existing launchd/systemd/schtasks unit at the
|
|
59
|
+
// managed binary.
|
|
60
|
+
//
|
|
61
|
+
// THIS IS A MIGRATION, and without it this package is a silent regression for
|
|
62
|
+
// every engineer who has run `autostart enable`. The unit bakes an ABSOLUTE
|
|
63
|
+
// path at enable time. Installs predating the managed-binary layout baked
|
|
64
|
+
// <pkg>/binaries/promptster-teams-<platform> — a path that no longer exists in
|
|
65
|
+
// the package, because the binary moved to a per-platform dependency and the
|
|
66
|
+
// wrapper stopped shipping binaries/ entirely. So `npm i -g` deletes the exact
|
|
67
|
+
// file the supervisor is pointed at.
|
|
68
|
+
//
|
|
69
|
+
// Nothing about that fails loudly. The running daemon holds its inode and keeps
|
|
70
|
+
// capturing, so the upgrade looks clean; the unit only breaks at the NEXT LOGIN,
|
|
71
|
+
// when launchd runs a path that is gone. Capture never comes back — the precise
|
|
72
|
+
// failure autostart exists to prevent.
|
|
73
|
+
//
|
|
74
|
+
// Delegated to the Go binary (`autostart repair`) rather than reimplemented
|
|
75
|
+
// here: this must not grow a plist writer, a systemd unit writer and a schtasks
|
|
76
|
+
// caller in Node, three platforms out of sync with the Go ones. `repair` is a
|
|
77
|
+
// no-op when autostart was never enabled and never exits non-zero.
|
|
78
|
+
function repairAutostart(managed) {
|
|
79
|
+
try {
|
|
80
|
+
const r = spawnSync(managed, ["autostart", "repair"], {
|
|
81
|
+
encoding: "utf8",
|
|
82
|
+
timeout: 30_000,
|
|
83
|
+
});
|
|
84
|
+
const out = `${r.stdout || ""}${r.stderr || ""}`.trim();
|
|
85
|
+
if (out) console.log(out);
|
|
86
|
+
} catch {
|
|
87
|
+
// Best-effort: a failed repair leaves autostart pointing at the old path,
|
|
88
|
+
// which the engineer can fix with `promptster-teams autostart enable`.
|
|
89
|
+
// Aborting the install over it would be strictly worse.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function main() {
|
|
94
|
+
// A project-local install must not touch the shared managed binary. The
|
|
95
|
+
// lockfile is a per-PROJECT pin; the managed binary is per-USER. Letting a
|
|
96
|
+
// local install write it means two repos pinning different versions fight
|
|
97
|
+
// over one file and neither gets what its lockfile selected — a worse bug
|
|
98
|
+
// than the npm-ls drift this whole design removes. Local installs keep
|
|
99
|
+
// running out of their own node_modules, exactly as before.
|
|
100
|
+
if (!isGlobalInstall()) {
|
|
101
|
+
console.log(
|
|
102
|
+
"promptster-teams: project-local install — leaving ~/.promptster-teams/bin alone " +
|
|
103
|
+
"and running the version your lockfile pins"
|
|
104
|
+
);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const bundled = bundledBinPath();
|
|
109
|
+
if (!bundled) {
|
|
110
|
+
// Either an unsupported platform, or the optionalDependency carrying the
|
|
111
|
+
// binary is absent. npm treats a missing optional dep as SUCCESS and says
|
|
112
|
+
// nothing, so this warning is the only signal the engineer will ever get
|
|
113
|
+
// that they have a CLI with no binary behind it. Say which package.
|
|
114
|
+
if (!PLATFORMS.includes(platformKey())) {
|
|
115
|
+
warn(`unsupported platform ${platformKey()} — no binary to install`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
warn(`${platformPackage()} is not installed, so there is no binary to install.`);
|
|
119
|
+
warn("If you used --omit=optional or --no-optional, reinstall without it.");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const managed = managedBinPath();
|
|
123
|
+
if (!managed) {
|
|
124
|
+
warn("could not resolve home directory — skipping binary install");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const incoming = binVersion(bundled);
|
|
129
|
+
const current = binVersion(managed);
|
|
130
|
+
|
|
131
|
+
if (!incoming) {
|
|
132
|
+
warn(`bundled binary at ${bundled} did not report a version — skipping`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Never downgrade. The managed binary self-updates forward on its own, so it
|
|
137
|
+
// is routinely NEWER than whatever version npm is installing (that is the
|
|
138
|
+
// normal steady state, not an error). Clobbering it would hand the daemon an
|
|
139
|
+
// older build that immediately re-updates — churn, plus a window on a version
|
|
140
|
+
// the engineer already moved past. Mirrors the Go updater's isNewer gate.
|
|
141
|
+
//
|
|
142
|
+
// A managed binary that cannot report a version (current === null) is treated
|
|
143
|
+
// as absent and overwritten: a corrupt or half-written file should be
|
|
144
|
+
// replaced, not preserved by a guard meant to protect a GOOD newer build.
|
|
145
|
+
if (current && !isNewer(incoming, current)) {
|
|
146
|
+
console.log(
|
|
147
|
+
`promptster-teams: ${managed} is ${current}; not replacing it with ${incoming}`
|
|
148
|
+
);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
fs.mkdirSync(path.dirname(managed), { recursive: true });
|
|
154
|
+
// Write to a temp file in the SAME directory, then rename: rename is atomic
|
|
155
|
+
// on POSIX, so a concurrent `promptster-teams` exec sees either the whole
|
|
156
|
+
// old binary or the whole new one, never a half-written file. Copying
|
|
157
|
+
// straight onto `managed` would also fail with ETXTBSY on Linux if the
|
|
158
|
+
// daemon is running.
|
|
159
|
+
const tmp = `${managed}.tmp-${process.pid}`;
|
|
160
|
+
fs.copyFileSync(bundled, tmp);
|
|
161
|
+
fs.chmodSync(tmp, 0o755);
|
|
162
|
+
|
|
163
|
+
// Re-check immediately before the swap. The guard above and this rename are
|
|
164
|
+
// not atomic with respect to the Go self-updater, which renames onto this
|
|
165
|
+
// same path from a live daemon: read 1.0.0 -> daemon installs 1.2.0 ->
|
|
166
|
+
// rename 1.1.0 over it, and the guard has been defeated.
|
|
167
|
+
//
|
|
168
|
+
// This narrows the window from "the whole copy" to the microseconds around
|
|
169
|
+
// the rename rather than closing it. Closing it needs a lock protocol shared
|
|
170
|
+
// by a Node script and a Go daemon, which is a lot of machinery for this
|
|
171
|
+
// failure: rename is atomic so the file is always ONE whole valid binary
|
|
172
|
+
// (never corrupt), the only cost is running an older version, and the daemon
|
|
173
|
+
// re-updates forward within one check interval (<=30m). Deliberate tradeoff,
|
|
174
|
+
// not an oversight — see CLAUDE.md.
|
|
175
|
+
const stillCurrent = binVersion(managed);
|
|
176
|
+
if (stillCurrent && !isNewer(incoming, stillCurrent)) {
|
|
177
|
+
fs.unlinkSync(tmp);
|
|
178
|
+
console.log(
|
|
179
|
+
`promptster-teams: ${managed} changed to ${stillCurrent} mid-install; leaving it`
|
|
180
|
+
);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
fs.renameSync(tmp, managed);
|
|
184
|
+
console.log(
|
|
185
|
+
`promptster-teams: installed ${incoming} to ${managed}${
|
|
186
|
+
current ? ` (was ${current})` : ""
|
|
187
|
+
}`
|
|
188
|
+
);
|
|
189
|
+
repairAutostart(managed);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
// Falls back to the bundled binary via bin/promptster-teams.js.
|
|
192
|
+
warn(`could not install to ${managed}: ${err.message}`);
|
|
193
|
+
warn("falling back to the bundled binary (npm ls may report a stale version)");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
main();
|
|
199
|
+
} catch (err) {
|
|
200
|
+
// Belt and braces: nothing here may abort an npm install.
|
|
201
|
+
warn(`postinstall skipped: ${err && err.message}`);
|
|
202
|
+
}
|
package/binaries/SHA256SUMS
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
2c8a4c5553788f5b6b781dcec1ab33fae2a0bff8f094370d9b0b143e975442d5 promptster-teams-linux-x64
|
|
2
|
-
1b2cfba34071904e43e42ee6b87ea4c442f7bc50e62ffe51c58623cbee1f9de5 promptster-teams-linux-arm64
|
|
3
|
-
e15413b163e1b5fac9254342881b2f6ae790dcfa433018cd4210750dc0df0133 promptster-teams-darwin-x64
|
|
4
|
-
1ced87baf08d4af80e0515b4206ccd6d110ffe311390ed69868b2451e8e8d4cf promptster-teams-darwin-arm64
|
|
5
|
-
86cd9607458ffb76de60d06919bde579f5d89f1dc625903645909c5a2373c7f1 promptster-teams-win32-x64.exe
|
|
6
|
-
40f6278f6ec4216b896081820fe2178f0ad96bc9f49cedb6ca8843f4c8d40f0f promptster-teams-win32-arm64.exe
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
untrusted comment: signature from minisign secret key
|
|
2
|
-
RUSPnqFjEKp3+S04GPMUnLjuEyer/6aoLJjkuc9K2bVb0eC+0S0muKBqw8x6NQnIfyI562lAQQNWkTxhv75vIzcQuvqCyPeDjQ8=
|
|
3
|
-
trusted comment: timestamp:1784117828 file:SHA256SUMS hashed
|
|
4
|
-
ym2SDj/OeLtNJIB3eyPWAF2PAWeNS8ckXRaNO0+Euxychcqs7+mpmJH4HwrRvhlO4MhE5m8dXyj0zWAJjC46Bg==
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|