@usesocial/cli 0.11.7 → 0.11.8
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/CHANGELOG.md +6 -0
- package/README.md +20 -0
- package/dist/index.mjs +540 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
|
-
import { createReadStream, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { createReadStream, existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
6
7
|
import { homedir } from "node:os";
|
|
7
|
-
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
8
8
|
import { parseArgs, styleText } from "node:util";
|
|
9
|
-
import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
9
|
+
import { chmod, lstat, mkdir, readFile, readdir, readlink, rm, writeFile } from "node:fs/promises";
|
|
10
10
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
11
11
|
import { createHash } from "node:crypto";
|
|
12
12
|
import { dirname as dirname$1, isAbsolute, posix, relative, sep } from "path";
|
|
@@ -15,6 +15,7 @@ import l__default, { createInterface } from "node:readline";
|
|
|
15
15
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
16
16
|
import process$1, { stdin, stdout } from "node:process";
|
|
17
17
|
import { ReadStream } from "node:tty";
|
|
18
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
18
19
|
//#region \0rolldown/runtime.js
|
|
19
20
|
var __create = Object.create;
|
|
20
21
|
var __defProp = Object.defineProperty;
|
|
@@ -11039,6 +11040,7 @@ pgTable("cli_session_grants", {
|
|
|
11039
11040
|
}, (table) => [index("cli_session_grants_user_idx").on(table.userId, table.createdAt.desc()), check("cli_session_grants_scope_alias_check", sqlCheckIn(table.scopeAlias, CLI_SCOPE_ALIASES$1))]);
|
|
11040
11041
|
pgTable("wrapped", {
|
|
11041
11042
|
id: text$1("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
|
11043
|
+
userId: text$1("user_id").references(() => user.id, { onDelete: "cascade" }),
|
|
11042
11044
|
platform: text$1("platform", { enum: PLATFORMS }).notNull().default("x"),
|
|
11043
11045
|
username: text$1("username").notNull(),
|
|
11044
11046
|
platformProfileId: text$1("platform_profile_id").notNull(),
|
|
@@ -11052,6 +11054,7 @@ pgTable("wrapped", {
|
|
|
11052
11054
|
updatedAt: timestamptz("updated_at").notNull().defaultNow()
|
|
11053
11055
|
}, (table) => [
|
|
11054
11056
|
uniqueIndex("wrapped_platform_profile_key").on(table.platform, table.platformProfileId),
|
|
11057
|
+
index("wrapped_user_platform_idx").on(table.userId, table.platform, table.updatedAt.desc()).where(sql`${table.userId} is not null`),
|
|
11055
11058
|
index("wrapped_platform_username_idx").on(table.platform, table.username),
|
|
11056
11059
|
check("wrapped_platform_check", sqlCheckIn(table.platform, PLATFORMS)),
|
|
11057
11060
|
check("wrapped_status_check", sqlCheckIn(table.status, WRAPPED_STATUSES))
|
|
@@ -23172,7 +23175,7 @@ function createEnv(opts) {
|
|
|
23172
23175
|
}
|
|
23173
23176
|
//#endregion
|
|
23174
23177
|
//#region package.json
|
|
23175
|
-
var version$1 = "0.11.
|
|
23178
|
+
var version$1 = "0.11.8";
|
|
23176
23179
|
//#endregion
|
|
23177
23180
|
//#region src/lib/env.ts
|
|
23178
23181
|
const URLWithTrailingSlash = url().transform(ensureTrailingSlash);
|
|
@@ -23682,6 +23685,9 @@ const readStdin = async () => {
|
|
|
23682
23685
|
const printLine = (value) => {
|
|
23683
23686
|
outputWriters.stdout(value);
|
|
23684
23687
|
};
|
|
23688
|
+
const printStderrLine = (value) => {
|
|
23689
|
+
outputWriters.stderr(value);
|
|
23690
|
+
};
|
|
23685
23691
|
const printData = (value) => {
|
|
23686
23692
|
outputWriters.stdout(JSON.stringify(value));
|
|
23687
23693
|
};
|
|
@@ -27121,24 +27127,24 @@ function matchProperty(property, propertyValues, warnFunction) {
|
|
|
27121
27127
|
if (["is_date_before"].includes(operator)) return overrideDate < parsedDate;
|
|
27122
27128
|
return overrideDate > parsedDate;
|
|
27123
27129
|
}
|
|
27124
|
-
case "semver_eq": return 0 === compareSemverTuples(parseSemver(String(overrideValue)), parseSemver(String(value)));
|
|
27125
|
-
case "semver_neq": return 0 !== compareSemverTuples(parseSemver(String(overrideValue)), parseSemver(String(value)));
|
|
27126
|
-
case "semver_gt": return compareSemverTuples(parseSemver(String(overrideValue)), parseSemver(String(value))) > 0;
|
|
27127
|
-
case "semver_gte": return compareSemverTuples(parseSemver(String(overrideValue)), parseSemver(String(value))) >= 0;
|
|
27128
|
-
case "semver_lt": return compareSemverTuples(parseSemver(String(overrideValue)), parseSemver(String(value))) < 0;
|
|
27129
|
-
case "semver_lte": return compareSemverTuples(parseSemver(String(overrideValue)), parseSemver(String(value))) <= 0;
|
|
27130
|
+
case "semver_eq": return 0 === compareSemverTuples(parseSemver$1(String(overrideValue)), parseSemver$1(String(value)));
|
|
27131
|
+
case "semver_neq": return 0 !== compareSemverTuples(parseSemver$1(String(overrideValue)), parseSemver$1(String(value)));
|
|
27132
|
+
case "semver_gt": return compareSemverTuples(parseSemver$1(String(overrideValue)), parseSemver$1(String(value))) > 0;
|
|
27133
|
+
case "semver_gte": return compareSemverTuples(parseSemver$1(String(overrideValue)), parseSemver$1(String(value))) >= 0;
|
|
27134
|
+
case "semver_lt": return compareSemverTuples(parseSemver$1(String(overrideValue)), parseSemver$1(String(value))) < 0;
|
|
27135
|
+
case "semver_lte": return compareSemverTuples(parseSemver$1(String(overrideValue)), parseSemver$1(String(value))) <= 0;
|
|
27130
27136
|
case "semver_tilde": {
|
|
27131
|
-
const overrideParsed = parseSemver(String(overrideValue));
|
|
27137
|
+
const overrideParsed = parseSemver$1(String(overrideValue));
|
|
27132
27138
|
const { lower, upper } = computeTildeBounds(String(value));
|
|
27133
27139
|
return compareSemverTuples(overrideParsed, lower) >= 0 && compareSemverTuples(overrideParsed, upper) < 0;
|
|
27134
27140
|
}
|
|
27135
27141
|
case "semver_caret": {
|
|
27136
|
-
const overrideParsed = parseSemver(String(overrideValue));
|
|
27142
|
+
const overrideParsed = parseSemver$1(String(overrideValue));
|
|
27137
27143
|
const { lower, upper } = computeCaretBounds(String(value));
|
|
27138
27144
|
return compareSemverTuples(overrideParsed, lower) >= 0 && compareSemverTuples(overrideParsed, upper) < 0;
|
|
27139
27145
|
}
|
|
27140
27146
|
case "semver_wildcard": {
|
|
27141
|
-
const overrideParsed = parseSemver(String(overrideValue));
|
|
27147
|
+
const overrideParsed = parseSemver$1(String(overrideValue));
|
|
27142
27148
|
const { lower, upper } = computeWildcardBounds(String(value));
|
|
27143
27149
|
return compareSemverTuples(overrideParsed, lower) >= 0 && compareSemverTuples(overrideParsed, upper) < 0;
|
|
27144
27150
|
}
|
|
@@ -27214,7 +27220,7 @@ function parseSemverNumericIdentifier(part, raw) {
|
|
|
27214
27220
|
if (part.length > 1 && "0" === part[0]) throw new InconclusiveMatchError(`Invalid semver: ${raw}`);
|
|
27215
27221
|
return parseInt(part, 10);
|
|
27216
27222
|
}
|
|
27217
|
-
function parseSemver(value) {
|
|
27223
|
+
function parseSemver$1(value) {
|
|
27218
27224
|
const baseVersion = String(value).trim().replace(/^[vV]/, "").split("-")[0].split("+")[0];
|
|
27219
27225
|
if (!baseVersion || baseVersion.startsWith(".")) throw new InconclusiveMatchError(`Invalid semver: ${value}`);
|
|
27220
27226
|
const parts = baseVersion.split(".");
|
|
@@ -27236,7 +27242,7 @@ function compareSemverTuples(a, b) {
|
|
|
27236
27242
|
return 0;
|
|
27237
27243
|
}
|
|
27238
27244
|
function computeTildeBounds(value) {
|
|
27239
|
-
const parsed = parseSemver(value);
|
|
27245
|
+
const parsed = parseSemver$1(value);
|
|
27240
27246
|
return {
|
|
27241
27247
|
lower: [
|
|
27242
27248
|
parsed[0],
|
|
@@ -27251,7 +27257,7 @@ function computeTildeBounds(value) {
|
|
|
27251
27257
|
};
|
|
27252
27258
|
}
|
|
27253
27259
|
function computeCaretBounds(value) {
|
|
27254
|
-
const [major, minor, patch] = parseSemver(value);
|
|
27260
|
+
const [major, minor, patch] = parseSemver$1(value);
|
|
27255
27261
|
const lower = [
|
|
27256
27262
|
major,
|
|
27257
27263
|
minor,
|
|
@@ -28471,7 +28477,7 @@ const outcomeForExitCode = (code) => {
|
|
|
28471
28477
|
}
|
|
28472
28478
|
};
|
|
28473
28479
|
/** Any non-empty value other than "0"/"false" counts as truthy. */
|
|
28474
|
-
const truthy = (value) => {
|
|
28480
|
+
const truthy$1 = (value) => {
|
|
28475
28481
|
if (!value) return false;
|
|
28476
28482
|
const normalized = value.trim().toLowerCase();
|
|
28477
28483
|
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
@@ -28480,7 +28486,7 @@ const truthy = (value) => {
|
|
|
28480
28486
|
* `enabled` is pure env — computed once, no I/O. When false, no client is
|
|
28481
28487
|
* constructed, the state file is never touched, and no notice is printed.
|
|
28482
28488
|
*/
|
|
28483
|
-
const computeEnabled = () => !!env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN && !truthy(env.DO_NOT_TRACK) && !truthy(env.SOCIAL_DO_NOT_TRACK) && !truthy(env.CI);
|
|
28489
|
+
const computeEnabled = () => !!env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN && !truthy$1(env.DO_NOT_TRACK) && !truthy$1(env.SOCIAL_DO_NOT_TRACK) && !truthy$1(env.CI);
|
|
28484
28490
|
const statePath = () => join(homedir(), ".social", "analytics.json");
|
|
28485
28491
|
/**
|
|
28486
28492
|
* Read existing state, or create a fresh anonymousId on first run. Returns
|
|
@@ -37161,6 +37167,516 @@ const schemaCommand = defineCommand({
|
|
|
37161
37167
|
}
|
|
37162
37168
|
});
|
|
37163
37169
|
//#endregion
|
|
37170
|
+
//#region src/update/check.ts
|
|
37171
|
+
const NPM_REGISTRY_URL = "https://registry.npmjs.org/@usesocial%2fcli";
|
|
37172
|
+
const SKILL_TREE_URL = "https://api.github.com/repos/usesocial/skill/git/trees/main:skills/social";
|
|
37173
|
+
const UPDATE_CACHE_TTL_MS = 1200 * 60 * 1e3;
|
|
37174
|
+
const UPDATE_NOTICE_TTL_MS = 1440 * 60 * 1e3;
|
|
37175
|
+
const INTERNAL_UPDATE_PROBE_ENV = "SOCIAL_INTERNAL_UPDATE_PROBE";
|
|
37176
|
+
_enum([
|
|
37177
|
+
"brew",
|
|
37178
|
+
"bun",
|
|
37179
|
+
"npm"
|
|
37180
|
+
]);
|
|
37181
|
+
const CLIUpdateStatus = _enum([
|
|
37182
|
+
"current",
|
|
37183
|
+
"update_available",
|
|
37184
|
+
"unknown"
|
|
37185
|
+
]);
|
|
37186
|
+
const SkillUpdateStatus = _enum([
|
|
37187
|
+
"current",
|
|
37188
|
+
"not_installed",
|
|
37189
|
+
"update_available",
|
|
37190
|
+
"unknown"
|
|
37191
|
+
]);
|
|
37192
|
+
const SkillScope = _enum(["project", "global"]);
|
|
37193
|
+
const NPMRegistryResponse = object({
|
|
37194
|
+
"dist-tags": object({ latest: string().optional() }).passthrough().optional(),
|
|
37195
|
+
versions: record(string(), object({ dist: object({
|
|
37196
|
+
integrity: string().optional(),
|
|
37197
|
+
tarball: string().optional()
|
|
37198
|
+
}).passthrough().optional() }).passthrough())
|
|
37199
|
+
}).passthrough();
|
|
37200
|
+
const GitHubTreeResponse = object({ sha: string() });
|
|
37201
|
+
const CacheFile = object({
|
|
37202
|
+
checkedAt: number().int().nonnegative(),
|
|
37203
|
+
cli: object({
|
|
37204
|
+
currentVersion: string(),
|
|
37205
|
+
latestVersion: string().nullable(),
|
|
37206
|
+
status: CLIUpdateStatus,
|
|
37207
|
+
updateCommand: string().nullable()
|
|
37208
|
+
}),
|
|
37209
|
+
notices: record(string(), number().int().nonnegative()).optional(),
|
|
37210
|
+
skill: object({
|
|
37211
|
+
localTreeSHA: string().optional(),
|
|
37212
|
+
remoteTreeSHA: string().optional(),
|
|
37213
|
+
scope: SkillScope.nullable(),
|
|
37214
|
+
status: SkillUpdateStatus,
|
|
37215
|
+
updateCommand: string().nullable()
|
|
37216
|
+
})
|
|
37217
|
+
}).strict();
|
|
37218
|
+
const internalPackageRoot = () => {
|
|
37219
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
|
37220
|
+
const leaf = basename(dir);
|
|
37221
|
+
return leaf === "dist" || leaf === "src" ? dirname(dir) : dirname(dirname(dir));
|
|
37222
|
+
};
|
|
37223
|
+
const updateCachePath = (home = process.env.HOME ?? homedir()) => join(home, ".social", "update-check.json");
|
|
37224
|
+
const defaultCommandRunner = (cmd, args, options) => {
|
|
37225
|
+
const result = spawnSync(cmd, args, {
|
|
37226
|
+
cwd: options?.cwd,
|
|
37227
|
+
encoding: "utf8",
|
|
37228
|
+
maxBuffer: 1024 * 1024,
|
|
37229
|
+
stdio: [
|
|
37230
|
+
"ignore",
|
|
37231
|
+
"pipe",
|
|
37232
|
+
"ignore"
|
|
37233
|
+
],
|
|
37234
|
+
timeout: 1e4
|
|
37235
|
+
});
|
|
37236
|
+
return {
|
|
37237
|
+
ok: result.status === 0,
|
|
37238
|
+
stdout: typeof result.stdout === "string" ? result.stdout : ""
|
|
37239
|
+
};
|
|
37240
|
+
};
|
|
37241
|
+
const truthy = (value) => {
|
|
37242
|
+
if (!value) return false;
|
|
37243
|
+
const normalized = value.trim().toLowerCase();
|
|
37244
|
+
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
37245
|
+
};
|
|
37246
|
+
const updateAwarenessEnabled = () => process.stderr.isTTY && !truthy(process.env.CI) && !truthy(process.env.DO_NOT_TRACK) && !truthy(process.env.SOCIAL_DO_NOT_TRACK);
|
|
37247
|
+
const parseSemver = (version) => {
|
|
37248
|
+
const match = /^v?(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(?:-(?<pre>[0-9A-Za-z.-]+))?/.exec(version);
|
|
37249
|
+
if (!match?.groups) return;
|
|
37250
|
+
return {
|
|
37251
|
+
major: Number(match.groups.major),
|
|
37252
|
+
minor: Number(match.groups.minor),
|
|
37253
|
+
patch: Number(match.groups.patch),
|
|
37254
|
+
prerelease: match.groups.pre?.split(".") ?? []
|
|
37255
|
+
};
|
|
37256
|
+
};
|
|
37257
|
+
const compareIdentifier = (left, right) => {
|
|
37258
|
+
const leftNumber = /^\d+$/.test(left) ? Number(left) : void 0;
|
|
37259
|
+
const rightNumber = /^\d+$/.test(right) ? Number(right) : void 0;
|
|
37260
|
+
if (leftNumber !== void 0 && rightNumber !== void 0) return Math.sign(leftNumber - rightNumber);
|
|
37261
|
+
if (leftNumber !== void 0) return -1;
|
|
37262
|
+
if (rightNumber !== void 0) return 1;
|
|
37263
|
+
return left.localeCompare(right);
|
|
37264
|
+
};
|
|
37265
|
+
const compareSemver = (left, right) => {
|
|
37266
|
+
const parsedLeft = parseSemver(left);
|
|
37267
|
+
const parsedRight = parseSemver(right);
|
|
37268
|
+
if (!(parsedLeft && parsedRight)) return left.localeCompare(right);
|
|
37269
|
+
for (const key of [
|
|
37270
|
+
"major",
|
|
37271
|
+
"minor",
|
|
37272
|
+
"patch"
|
|
37273
|
+
]) {
|
|
37274
|
+
const delta = parsedLeft[key] - parsedRight[key];
|
|
37275
|
+
if (delta !== 0) return Math.sign(delta);
|
|
37276
|
+
}
|
|
37277
|
+
if (parsedLeft.prerelease.length === 0 && parsedRight.prerelease.length > 0) return 1;
|
|
37278
|
+
if (parsedLeft.prerelease.length > 0 && parsedRight.prerelease.length === 0) return -1;
|
|
37279
|
+
const length = Math.max(parsedLeft.prerelease.length, parsedRight.prerelease.length);
|
|
37280
|
+
for (let index = 0; index < length; index += 1) {
|
|
37281
|
+
const leftPart = parsedLeft.prerelease[index];
|
|
37282
|
+
const rightPart = parsedRight.prerelease[index];
|
|
37283
|
+
if (leftPart === void 0) return -1;
|
|
37284
|
+
if (rightPart === void 0) return 1;
|
|
37285
|
+
const delta = compareIdentifier(leftPart, rightPart);
|
|
37286
|
+
if (delta !== 0) return delta;
|
|
37287
|
+
}
|
|
37288
|
+
return 0;
|
|
37289
|
+
};
|
|
37290
|
+
const detectPackageManager = (packageRoot) => {
|
|
37291
|
+
const normalized = packageRoot.replaceAll("\\", "/");
|
|
37292
|
+
if (normalized.includes("/Cellar/usesocial/") || normalized.includes("/Cellar/cli/") || normalized.includes("/Homebrew/")) return "brew";
|
|
37293
|
+
if (normalized.includes("/.bun/install/global/")) return "bun";
|
|
37294
|
+
return "npm";
|
|
37295
|
+
};
|
|
37296
|
+
const cliUpdateCommand = (manager) => {
|
|
37297
|
+
if (manager === "brew") return "brew upgrade usesocial/tap/cli";
|
|
37298
|
+
if (manager === "bun") return "bun install -g @usesocial/cli@latest";
|
|
37299
|
+
return "npm install -g @usesocial/cli@latest";
|
|
37300
|
+
};
|
|
37301
|
+
const skillUpdateRunner = (manager) => manager === "bun" ? "bunx --bun" : "npx --yes";
|
|
37302
|
+
const skillUpdateCommand = (scope, manager) => `${skillUpdateRunner(manager)} skills update social --${scope} --yes`;
|
|
37303
|
+
const parseNPMUpdateResult = (value, currentVersion, manager) => {
|
|
37304
|
+
const parsed = NPMRegistryResponse.safeParse(value);
|
|
37305
|
+
if (!parsed.success) return {
|
|
37306
|
+
currentVersion,
|
|
37307
|
+
latestVersion: null,
|
|
37308
|
+
status: "unknown",
|
|
37309
|
+
updateCommand: null
|
|
37310
|
+
};
|
|
37311
|
+
const latestVersion = parsed.data["dist-tags"]?.latest ?? null;
|
|
37312
|
+
if (!latestVersion || compareSemver(latestVersion, currentVersion) <= 0) return {
|
|
37313
|
+
currentVersion,
|
|
37314
|
+
latestVersion,
|
|
37315
|
+
status: "current",
|
|
37316
|
+
updateCommand: null
|
|
37317
|
+
};
|
|
37318
|
+
const latestPackage = parsed.data.versions[latestVersion];
|
|
37319
|
+
if (!(latestPackage?.dist?.tarball && latestPackage.dist.integrity)) return {
|
|
37320
|
+
currentVersion,
|
|
37321
|
+
latestVersion,
|
|
37322
|
+
status: "unknown",
|
|
37323
|
+
updateCommand: null
|
|
37324
|
+
};
|
|
37325
|
+
return {
|
|
37326
|
+
currentVersion,
|
|
37327
|
+
latestVersion,
|
|
37328
|
+
status: "update_available",
|
|
37329
|
+
updateCommand: cliUpdateCommand(manager)
|
|
37330
|
+
};
|
|
37331
|
+
};
|
|
37332
|
+
const unknownCLIStatus = () => ({
|
|
37333
|
+
currentVersion: VERSION,
|
|
37334
|
+
latestVersion: null,
|
|
37335
|
+
status: "unknown",
|
|
37336
|
+
updateCommand: null
|
|
37337
|
+
});
|
|
37338
|
+
const checkCLIUpdate = async (deps = {}) => {
|
|
37339
|
+
const packageRoot = deps.packageRoot ?? internalPackageRoot();
|
|
37340
|
+
const fetchImpl = deps.fetch ?? fetch;
|
|
37341
|
+
try {
|
|
37342
|
+
const response = await fetchImpl(NPM_REGISTRY_URL, { headers: { accept: "application/json" } });
|
|
37343
|
+
if (!response.ok) return unknownCLIStatus();
|
|
37344
|
+
return parseNPMUpdateResult(await response.json(), VERSION, detectPackageManager(packageRoot));
|
|
37345
|
+
} catch {
|
|
37346
|
+
return unknownCLIStatus();
|
|
37347
|
+
}
|
|
37348
|
+
};
|
|
37349
|
+
const readJSON = async (path) => {
|
|
37350
|
+
try {
|
|
37351
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
37352
|
+
} catch {
|
|
37353
|
+
return;
|
|
37354
|
+
}
|
|
37355
|
+
};
|
|
37356
|
+
const lockSkillFrom = (value) => {
|
|
37357
|
+
if (!value || typeof value !== "object") return;
|
|
37358
|
+
const skills = value.skills;
|
|
37359
|
+
if (!skills || typeof skills !== "object") return;
|
|
37360
|
+
const social = skills.social;
|
|
37361
|
+
if (!social || typeof social !== "object") return;
|
|
37362
|
+
const record = social;
|
|
37363
|
+
return {
|
|
37364
|
+
source: typeof record.source === "string" ? record.source : void 0,
|
|
37365
|
+
skillPath: typeof record.skillPath === "string" ? record.skillPath : void 0
|
|
37366
|
+
};
|
|
37367
|
+
};
|
|
37368
|
+
const skillFolderFromLock = (lockPath, skill) => {
|
|
37369
|
+
if (!skill?.skillPath) return;
|
|
37370
|
+
return join(dirname(lockPath), dirname(skill.skillPath));
|
|
37371
|
+
};
|
|
37372
|
+
const candidateFromLock = async (lockPath, scope) => {
|
|
37373
|
+
const skill = lockSkillFrom(await readJSON(lockPath));
|
|
37374
|
+
const folder = skillFolderFromLock(lockPath, skill);
|
|
37375
|
+
if (!folder) return;
|
|
37376
|
+
return {
|
|
37377
|
+
folder,
|
|
37378
|
+
scope,
|
|
37379
|
+
source: skill?.source
|
|
37380
|
+
};
|
|
37381
|
+
};
|
|
37382
|
+
const folderFromListEntry = (entry) => {
|
|
37383
|
+
if (!entry || typeof entry !== "object") return;
|
|
37384
|
+
const record = entry;
|
|
37385
|
+
for (const key of [
|
|
37386
|
+
"folder",
|
|
37387
|
+
"path",
|
|
37388
|
+
"directory",
|
|
37389
|
+
"dir"
|
|
37390
|
+
]) if (typeof record[key] === "string") return record[key];
|
|
37391
|
+
if (typeof record.skillPath === "string") return dirname(record.skillPath);
|
|
37392
|
+
};
|
|
37393
|
+
const listEntriesFrom = (value) => {
|
|
37394
|
+
if (Array.isArray(value)) return value;
|
|
37395
|
+
if (!value || typeof value !== "object") return [];
|
|
37396
|
+
const record = value;
|
|
37397
|
+
if (Array.isArray(record.skills)) return record.skills;
|
|
37398
|
+
if (record.skills && typeof record.skills === "object") return Object.entries(record.skills).map(([name, skill]) => skill && typeof skill === "object" ? {
|
|
37399
|
+
name,
|
|
37400
|
+
...skill
|
|
37401
|
+
} : { name });
|
|
37402
|
+
return [];
|
|
37403
|
+
};
|
|
37404
|
+
const candidateFromSkillsList = (rawJSON, lockPath, scope) => {
|
|
37405
|
+
try {
|
|
37406
|
+
const folder = folderFromListEntry(listEntriesFrom(JSON.parse(rawJSON)).find((entry) => {
|
|
37407
|
+
if (!entry || typeof entry !== "object") return false;
|
|
37408
|
+
const record = entry;
|
|
37409
|
+
return record.name === "social" || record.id === "social";
|
|
37410
|
+
}));
|
|
37411
|
+
if (!folder) return;
|
|
37412
|
+
return {
|
|
37413
|
+
folder: folder.startsWith("/") ? folder : join(dirname(lockPath), folder),
|
|
37414
|
+
scope
|
|
37415
|
+
};
|
|
37416
|
+
} catch {
|
|
37417
|
+
return;
|
|
37418
|
+
}
|
|
37419
|
+
};
|
|
37420
|
+
const installedSkillCandidate = async (deps) => {
|
|
37421
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
37422
|
+
const home = deps.home ?? process.env.HOME ?? homedir();
|
|
37423
|
+
const runCommand = deps.runCommand ?? defaultCommandRunner;
|
|
37424
|
+
const candidates = [{
|
|
37425
|
+
args: ["list", "--json"],
|
|
37426
|
+
lockPath: join(cwd, "skills-lock.json"),
|
|
37427
|
+
scope: "project"
|
|
37428
|
+
}, {
|
|
37429
|
+
args: [
|
|
37430
|
+
"list",
|
|
37431
|
+
"--json",
|
|
37432
|
+
"--global"
|
|
37433
|
+
],
|
|
37434
|
+
lockPath: join(home, ".agents", ".skill-lock.json"),
|
|
37435
|
+
scope: "global"
|
|
37436
|
+
}];
|
|
37437
|
+
for (const candidate of candidates) {
|
|
37438
|
+
const fromLock = await candidateFromLock(candidate.lockPath, candidate.scope);
|
|
37439
|
+
if (fromLock?.source === "usesocial/skill" && existsSync(fromLock.folder)) return fromLock;
|
|
37440
|
+
const listed = runCommand("skills", candidate.args, { cwd });
|
|
37441
|
+
if (!listed.ok) continue;
|
|
37442
|
+
const fromList = candidateFromSkillsList(listed.stdout, candidate.lockPath, candidate.scope);
|
|
37443
|
+
if (!fromList) continue;
|
|
37444
|
+
const lock = lockSkillFrom(await readJSON(candidate.lockPath));
|
|
37445
|
+
if (lock?.source !== "usesocial/skill") continue;
|
|
37446
|
+
return {
|
|
37447
|
+
...fromList,
|
|
37448
|
+
source: lock.source
|
|
37449
|
+
};
|
|
37450
|
+
}
|
|
37451
|
+
};
|
|
37452
|
+
const hashGitObject = (type, body) => createHash("sha1").update(Buffer.from(`${type} ${body.length}\0`)).update(body).digest("hex");
|
|
37453
|
+
const gitBlobHash = (content) => hashGitObject("blob", content);
|
|
37454
|
+
const compareTreeEntry = (left, right) => Buffer.compare(Buffer.from(left.sortName), Buffer.from(right.sortName));
|
|
37455
|
+
const gitTreeHashForFolder = async (folder) => {
|
|
37456
|
+
const dirents = await readdir(folder, { withFileTypes: true });
|
|
37457
|
+
const entries = [];
|
|
37458
|
+
for (const dirent of dirents) {
|
|
37459
|
+
if (dirent.name === ".git") continue;
|
|
37460
|
+
const path = join(folder, dirent.name);
|
|
37461
|
+
const stat = await lstat(path);
|
|
37462
|
+
if (stat.isDirectory()) {
|
|
37463
|
+
entries.push({
|
|
37464
|
+
mode: "40000",
|
|
37465
|
+
name: dirent.name,
|
|
37466
|
+
sha: await gitTreeHashForFolder(path),
|
|
37467
|
+
sortName: `${dirent.name}/`
|
|
37468
|
+
});
|
|
37469
|
+
continue;
|
|
37470
|
+
}
|
|
37471
|
+
if (stat.isSymbolicLink()) {
|
|
37472
|
+
entries.push({
|
|
37473
|
+
mode: "120000",
|
|
37474
|
+
name: dirent.name,
|
|
37475
|
+
sha: gitBlobHash(Buffer.from(await readlink(path))),
|
|
37476
|
+
sortName: dirent.name
|
|
37477
|
+
});
|
|
37478
|
+
continue;
|
|
37479
|
+
}
|
|
37480
|
+
if (stat.isFile()) {
|
|
37481
|
+
const executable = [
|
|
37482
|
+
1,
|
|
37483
|
+
8,
|
|
37484
|
+
64
|
|
37485
|
+
].some((bit) => Math.floor(stat.mode / bit) % 2 === 1);
|
|
37486
|
+
entries.push({
|
|
37487
|
+
mode: executable ? "100755" : "100644",
|
|
37488
|
+
name: dirent.name,
|
|
37489
|
+
sha: gitBlobHash(await readFile(path)),
|
|
37490
|
+
sortName: dirent.name
|
|
37491
|
+
});
|
|
37492
|
+
}
|
|
37493
|
+
}
|
|
37494
|
+
return hashGitObject("tree", Buffer.concat(entries.sort(compareTreeEntry).map((entry) => Buffer.concat([Buffer.from(`${entry.mode} ${entry.name}\0`), Buffer.from(entry.sha, "hex")]))));
|
|
37495
|
+
};
|
|
37496
|
+
const remoteSkillTreeSHA = async (fetchImpl) => {
|
|
37497
|
+
const response = await fetchImpl(SKILL_TREE_URL, { headers: { accept: "application/vnd.github+json" } });
|
|
37498
|
+
if (!response.ok) return;
|
|
37499
|
+
const parsed = GitHubTreeResponse.safeParse(await response.json());
|
|
37500
|
+
return parsed.success ? parsed.data.sha : void 0;
|
|
37501
|
+
};
|
|
37502
|
+
const unknownSkillStatus = () => ({
|
|
37503
|
+
scope: null,
|
|
37504
|
+
status: "unknown",
|
|
37505
|
+
updateCommand: null
|
|
37506
|
+
});
|
|
37507
|
+
const checkSkillUpdate = async (deps = {}) => {
|
|
37508
|
+
const manager = detectPackageManager(deps.packageRoot ?? internalPackageRoot());
|
|
37509
|
+
const install = await installedSkillCandidate(deps);
|
|
37510
|
+
if (!install) return {
|
|
37511
|
+
scope: null,
|
|
37512
|
+
status: "not_installed",
|
|
37513
|
+
updateCommand: null
|
|
37514
|
+
};
|
|
37515
|
+
if (install.source !== "usesocial/skill") return unknownSkillStatus();
|
|
37516
|
+
try {
|
|
37517
|
+
const fetchImpl = deps.fetch ?? fetch;
|
|
37518
|
+
const [localSHA, remoteSHA] = await Promise.all([gitTreeHashForFolder(install.folder), remoteSkillTreeSHA(fetchImpl)]);
|
|
37519
|
+
if (!remoteSHA) return {
|
|
37520
|
+
scope: install.scope,
|
|
37521
|
+
status: "unknown",
|
|
37522
|
+
updateCommand: null
|
|
37523
|
+
};
|
|
37524
|
+
return {
|
|
37525
|
+
localTreeSHA: localSHA,
|
|
37526
|
+
remoteTreeSHA: remoteSHA,
|
|
37527
|
+
scope: install.scope,
|
|
37528
|
+
status: localSHA === remoteSHA ? "current" : "update_available",
|
|
37529
|
+
updateCommand: localSHA === remoteSHA ? null : skillUpdateCommand(install.scope, manager)
|
|
37530
|
+
};
|
|
37531
|
+
} catch {
|
|
37532
|
+
return {
|
|
37533
|
+
scope: install.scope,
|
|
37534
|
+
status: "unknown",
|
|
37535
|
+
updateCommand: null
|
|
37536
|
+
};
|
|
37537
|
+
}
|
|
37538
|
+
};
|
|
37539
|
+
const refreshUpdateStatus = async (deps = {}) => {
|
|
37540
|
+
const [cli, skill] = await Promise.all([checkCLIUpdate(deps), checkSkillUpdate(deps)]);
|
|
37541
|
+
return {
|
|
37542
|
+
checkedAt: deps.now?.() ?? Date.now(),
|
|
37543
|
+
cli,
|
|
37544
|
+
skill
|
|
37545
|
+
};
|
|
37546
|
+
};
|
|
37547
|
+
const readCachedUpdateStatus = async (home) => {
|
|
37548
|
+
try {
|
|
37549
|
+
const parsed = CacheFile.safeParse(JSON.parse(await readFile(updateCachePath(home), "utf8")));
|
|
37550
|
+
return parsed.success ? parsed.data : void 0;
|
|
37551
|
+
} catch {
|
|
37552
|
+
return;
|
|
37553
|
+
}
|
|
37554
|
+
};
|
|
37555
|
+
const writeCachedUpdateStatus = async (home, status) => {
|
|
37556
|
+
const path = updateCachePath(home);
|
|
37557
|
+
await mkdir(dirname(path), { recursive: true });
|
|
37558
|
+
await writeFile(path, `${JSON.stringify(status, null, 2)}\n`, { mode: 384 });
|
|
37559
|
+
await chmod(path, 384);
|
|
37560
|
+
};
|
|
37561
|
+
const refreshUpdateStatusCache = async (deps = {}) => {
|
|
37562
|
+
const status = await refreshUpdateStatus(deps);
|
|
37563
|
+
const cached = await readCachedUpdateStatus(deps.home);
|
|
37564
|
+
await writeCachedUpdateStatus(deps.home, {
|
|
37565
|
+
...status,
|
|
37566
|
+
notices: cached?.notices
|
|
37567
|
+
});
|
|
37568
|
+
return status;
|
|
37569
|
+
};
|
|
37570
|
+
const isUpdateCacheStale = (cache, now) => !cache || now - cache.checkedAt >= UPDATE_CACHE_TTL_MS;
|
|
37571
|
+
const noticeDue = (cache, key, now) => now - (cache.notices?.[key] ?? 0) >= UPDATE_NOTICE_TTL_MS;
|
|
37572
|
+
const withNotice = (cache, key, now) => ({
|
|
37573
|
+
...cache,
|
|
37574
|
+
notices: {
|
|
37575
|
+
...cache.notices ?? {},
|
|
37576
|
+
[key]: now
|
|
37577
|
+
}
|
|
37578
|
+
});
|
|
37579
|
+
const noticeLinesFor = (cache, now) => {
|
|
37580
|
+
const notices = [];
|
|
37581
|
+
if (cache.cli.status === "update_available" && cache.cli.latestVersion && cache.cli.updateCommand) {
|
|
37582
|
+
const key = `cli:${cache.cli.latestVersion}`;
|
|
37583
|
+
if (noticeDue(cache, key, now)) notices.push({
|
|
37584
|
+
key,
|
|
37585
|
+
line: `Update available: social CLI ${cache.cli.latestVersion} (current ${cache.cli.currentVersion}). Run: ${cache.cli.updateCommand}`
|
|
37586
|
+
});
|
|
37587
|
+
}
|
|
37588
|
+
if (cache.skill.status === "update_available" && cache.skill.scope && cache.skill.updateCommand && cache.skill.remoteTreeSHA) {
|
|
37589
|
+
const key = `skill:${cache.skill.scope}:${cache.skill.remoteTreeSHA}`;
|
|
37590
|
+
if (noticeDue(cache, key, now)) notices.push({
|
|
37591
|
+
key,
|
|
37592
|
+
line: `Update available: social skill (${cache.skill.scope}). Run: ${cache.skill.updateCommand}`
|
|
37593
|
+
});
|
|
37594
|
+
}
|
|
37595
|
+
return notices;
|
|
37596
|
+
};
|
|
37597
|
+
const spawnInternalProbe = (packageRoot) => {
|
|
37598
|
+
const sourceEntry = join(packageRoot, "src", "index.ts");
|
|
37599
|
+
const distEntry = join(packageRoot, "bin", "social.mjs");
|
|
37600
|
+
const entry = process.versions.bun && existsSync(sourceEntry) ? sourceEntry : distEntry;
|
|
37601
|
+
spawn(process.execPath, [entry], {
|
|
37602
|
+
detached: true,
|
|
37603
|
+
env: {
|
|
37604
|
+
...process.env,
|
|
37605
|
+
[INTERNAL_UPDATE_PROBE_ENV]: "1"
|
|
37606
|
+
},
|
|
37607
|
+
stdio: "ignore"
|
|
37608
|
+
}).unref();
|
|
37609
|
+
};
|
|
37610
|
+
const maybeRunUpdateAwareness = async (deps = {}) => {
|
|
37611
|
+
if (!updateAwarenessEnabled()) return;
|
|
37612
|
+
const now = deps.now?.() ?? Date.now();
|
|
37613
|
+
const home = deps.home;
|
|
37614
|
+
const cache = await readCachedUpdateStatus(home);
|
|
37615
|
+
if (cache) {
|
|
37616
|
+
let next = cache;
|
|
37617
|
+
for (const notice of noticeLinesFor(cache, now)) {
|
|
37618
|
+
printStderrLine(notice.line);
|
|
37619
|
+
next = withNotice(next, notice.key, now);
|
|
37620
|
+
}
|
|
37621
|
+
if (next !== cache) await writeCachedUpdateStatus(home, next);
|
|
37622
|
+
}
|
|
37623
|
+
if (isUpdateCacheStale(cache, now)) spawnInternalProbe(deps.packageRoot ?? internalPackageRoot());
|
|
37624
|
+
};
|
|
37625
|
+
const runInternalUpdateProbeIfRequested = async (deps = {}) => {
|
|
37626
|
+
if (process.env[INTERNAL_UPDATE_PROBE_ENV] !== "1") return false;
|
|
37627
|
+
try {
|
|
37628
|
+
await refreshUpdateStatusCache(deps);
|
|
37629
|
+
} catch {}
|
|
37630
|
+
return true;
|
|
37631
|
+
};
|
|
37632
|
+
const updateCommand = defineCommand({
|
|
37633
|
+
meta: commandMeta({
|
|
37634
|
+
name: "update",
|
|
37635
|
+
description: "Check for social CLI and skill updates.",
|
|
37636
|
+
contract: localCLIContract({
|
|
37637
|
+
outputSchema: object({
|
|
37638
|
+
cli: object({
|
|
37639
|
+
currentVersion: string(),
|
|
37640
|
+
latestVersion: string().nullable(),
|
|
37641
|
+
status: _enum([
|
|
37642
|
+
"current",
|
|
37643
|
+
"update_available",
|
|
37644
|
+
"unknown"
|
|
37645
|
+
]),
|
|
37646
|
+
updateCommand: string().nullable()
|
|
37647
|
+
}),
|
|
37648
|
+
skill: object({
|
|
37649
|
+
scope: _enum(["project", "global"]).nullable(),
|
|
37650
|
+
status: _enum([
|
|
37651
|
+
"current",
|
|
37652
|
+
"not_installed",
|
|
37653
|
+
"update_available",
|
|
37654
|
+
"unknown"
|
|
37655
|
+
]),
|
|
37656
|
+
updateCommand: string().nullable()
|
|
37657
|
+
})
|
|
37658
|
+
}),
|
|
37659
|
+
responseShaping: { formats: ["json"] }
|
|
37660
|
+
})
|
|
37661
|
+
}),
|
|
37662
|
+
run: async () => {
|
|
37663
|
+
const status = await refreshUpdateStatus();
|
|
37664
|
+
printData({
|
|
37665
|
+
cli: {
|
|
37666
|
+
currentVersion: status.cli.currentVersion,
|
|
37667
|
+
latestVersion: status.cli.latestVersion,
|
|
37668
|
+
status: status.cli.status,
|
|
37669
|
+
updateCommand: status.cli.updateCommand
|
|
37670
|
+
},
|
|
37671
|
+
skill: {
|
|
37672
|
+
scope: status.skill.scope,
|
|
37673
|
+
status: status.skill.status,
|
|
37674
|
+
updateCommand: status.skill.updateCommand
|
|
37675
|
+
}
|
|
37676
|
+
});
|
|
37677
|
+
}
|
|
37678
|
+
});
|
|
37679
|
+
//#endregion
|
|
37164
37680
|
//#region src/index.ts
|
|
37165
37681
|
const originalEmitWarning = process.emitWarning.bind(process);
|
|
37166
37682
|
process.emitWarning = ((warning, ...rest) => {
|
|
@@ -37351,6 +37867,7 @@ const createSocialCommand = (deps = createCLIDeps()) => {
|
|
|
37351
37867
|
subCommands: {
|
|
37352
37868
|
account: accountCommand,
|
|
37353
37869
|
schema: schemaCommand,
|
|
37870
|
+
update: updateCommand,
|
|
37354
37871
|
x: xCommand,
|
|
37355
37872
|
linkedin: linkedinCommand,
|
|
37356
37873
|
feedback: feedbackCommand
|
|
@@ -37397,6 +37914,10 @@ const runSocial = async ({ command = createSocialCommand(), now = Date.now, rawA
|
|
|
37397
37914
|
captureCommandException(error, exitCode, telemetry, now);
|
|
37398
37915
|
return await finish(exitCode, telemetry, now);
|
|
37399
37916
|
}
|
|
37917
|
+
if (path[0] !== "update") await maybeRunUpdateAwareness({
|
|
37918
|
+
now,
|
|
37919
|
+
packageRoot: dirname(dirname(fileURLToPath(import.meta.url)))
|
|
37920
|
+
});
|
|
37400
37921
|
return await finish(0, telemetry, now);
|
|
37401
37922
|
};
|
|
37402
37923
|
const isEPIPEError = (error) => error instanceof Error && "code" in error && error.code === "EPIPE";
|
|
@@ -37447,6 +37968,7 @@ const installProcessHandlers = (telemetry) => {
|
|
|
37447
37968
|
process.stderr.on("error", handlePipeError);
|
|
37448
37969
|
};
|
|
37449
37970
|
const main = async () => {
|
|
37971
|
+
if (await runInternalUpdateProbeIfRequested()) process.exit(0);
|
|
37450
37972
|
installProcessHandlers(analytics);
|
|
37451
37973
|
process.exit(await runSocial());
|
|
37452
37974
|
};
|