Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:

permissions:
contents: write
id-token: write

concurrency:
group: release
Expand Down Expand Up @@ -82,12 +83,20 @@ jobs:
--mode "$RELEASE_MODE"
--notes-file "${{ steps.release.outputs.release_notes_path }}"

- name: Stamp server.json version
if: steps.release.outputs.skip != 'true'
run: |
VERSION="${{ steps.release.outputs.version }}"
jq --arg v "$VERSION" \
'.version = $v | .packages[0].version = $v' \
server.json > server.tmp && mv server.tmp server.json

- name: Create release commit and tag
if: steps.release.outputs.skip != 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add package.json package-lock.json packages/core/package.json packages/cli/package.json packages/mcp/package.json
git add package.json package-lock.json packages/core/package.json packages/cli/package.json packages/mcp/package.json server.json
git commit -m "chore(release): publish ${{ steps.release.outputs.version }} [skip ci]"
git tag -a "${{ steps.release.outputs.tag }}" -m "Release ${{ steps.release.outputs.tag }}"

Expand Down Expand Up @@ -123,3 +132,23 @@ jobs:
make_latest: true
name: ${{ steps.release.outputs.tag }}
tag_name: ${{ steps.release.outputs.tag }}

- name: Publish to MCP Registry
if: steps.release.outputs.skip != 'true'
continue-on-error: true
run: |
curl -fsSL "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz" \
| tar xz mcp-publisher
./mcp-publisher login github-oidc
./mcp-publisher publish

- name: Notify Smithery
if: steps.release.outputs.skip != 'true' && env.SMITHERY_API_KEY != ''
env:
SMITHERY_API_KEY: ${{ secrets.SMITHERY_API_KEY }}
continue-on-error: true
run: |
curl -sf -X POST "https://api.smithery.ai/servers/linkedin-buddy/releases" \
-H "Authorization: Bearer $SMITHERY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"repo","gitUrl":"https://github.com/${{ github.repository }}","ref":"refs/tags/${{ steps.release.outputs.tag }}"}'
16 changes: 16 additions & 0 deletions .mcpbignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
*.test.ts
__tests__/
src/
docs/
scripts/
.github/
.worktrees/
node_modules/
*.md
!README.md
.env*
*.sqlite
coverage/
.eslint*
tsconfig*.json
vitest*.ts
6 changes: 6 additions & 0 deletions .opencode/todo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Mission Tasks

## Task List

[ ] *Start your mission by creating a task list

4 changes: 4 additions & 0 deletions glama.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "https://glama.ai/mcp/schemas/server.json",
"maintainers": ["sigvardt"]
}
93 changes: 93 additions & 0 deletions packages/cli/src/bin/linkedin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@ import {
importSessionState,
buildFeedbackHintMessage,
clearRateLimitState,
checkForUpdate,
createLocalDataDeletionPlan,
createEmptyFixtureManifest,
createFeedbackTechnicalContext,
buildFixtureRouteKey,
buildLinkedInImagePersonaFromProfileSeed,
buildUpdateCommand,
detectInstallMethod,
evaluateDraftQuality,
FEEDBACK_TYPES,
formatFeedbackDisplayPath,
Expand Down Expand Up @@ -95,6 +98,7 @@ import {
resolveLegacyRateLimitStateFilePath,
resolvePrivacyConfig,
resolveSchedulerConfig,
resolveUpdateCheckConfig,
runLinkedInWriteValidation,
runReadOnlyLinkedInLiveValidation,
submitFeedback,
Expand Down Expand Up @@ -127,6 +131,7 @@ import {
type SearchResult,
type SelectorAuditInput,
type SelectorAuditReport,
type UpdateCheckResult,
type WebhookDeliveryAttemptStatus,
type WebhookSubscriptionStatus,
type FeedbackType,
Expand Down Expand Up @@ -251,6 +256,7 @@ const TOTAL_WRITE_VALIDATION_ACTIONS = LINKEDIN_WRITE_VALIDATION_ACTIONS.length;
let cliEvasionEnabled = true;
let cliEvasionLevel: string | undefined;
let cliSelectorLocale: string | undefined;
let readUpdateCheckEnabled = (): boolean => true;
let activeCliInvocation:
| {
commandName: string;
Expand Down Expand Up @@ -1654,6 +1660,34 @@ async function maybeEmitCliFeedbackHint(error?: unknown): Promise<void> {
}
}

async function maybeShowUpdateNotification(): Promise<void> {
try {
if (!readUpdateCheckEnabled()) {
return;
}

const config = resolveUpdateCheckConfig({ timeoutMs: 2_000 });
if (!config.enabled) {
return;
}

const result: UpdateCheckResult = await checkForUpdate(
config,
packageJson.version,
);
if (!result.updateAvailable) {
return;
}

process.stderr.write(
`\n Update available: ${result.currentVersion} → ${result.latestVersion}\n` +
` Run \`${result.updateCommand}\` to update.\n\n`,
);
} catch {
return;
}
}

async function pathExists(targetPath: string): Promise<boolean> {
try {
await access(targetPath);
Expand Down Expand Up @@ -9596,6 +9630,10 @@ export function createCliProgram(): Command {
.name("linkedin")
.description("LinkedIn Buddy CLI")
.version(packageJson.version)
.option(
"--no-update-check",
"Disable automatic update check for this command",
)
.option(
"--cdp-url <url>",
"Connect to existing browser via CDP endpoint (e.g., http://127.0.0.1:18800)",
Expand Down Expand Up @@ -9635,6 +9673,11 @@ export function createCliProgram(): Command {
: undefined;
};

readUpdateCheckEnabled = function readUpdateCheckEnabled(): boolean {
const options = program.opts<{ updateCheck?: boolean }>();
return options.updateCheck !== false;
};

const readSelectorLocale = (): string | undefined => {
const options = program.opts<{ selectorLocale?: string }>();
return typeof options.selectorLocale === "string" &&
Expand Down Expand Up @@ -9669,6 +9712,7 @@ export function createCliProgram(): Command {

program.hook("postAction", async () => {
await maybeEmitCliFeedbackHint();
await maybeShowUpdateNotification();
});

program
Expand Down Expand Up @@ -13402,6 +13446,54 @@ export function createCliProgram(): Command {
}
});

program
.command("update")
.description("Check for updates and optionally install the latest version")
.option("--check-only", "Only check for updates without installing", false)
.action(async (options: { checkOnly: boolean }) => {
const config = resolveUpdateCheckConfig({ cacheTtlMs: 0, timeoutMs: 10_000 });
const result: UpdateCheckResult = await checkForUpdate(
{ ...config, enabled: true },
packageJson.version,
);

if (options.checkOnly) {
console.log(JSON.stringify(result, null, 2));
return;
}

console.log(`Current version: ${result.currentVersion}`);
console.log(`Latest version: ${result.latestVersion}`);

if (!result.updateAvailable) {
console.log("\nYou are already on the latest version.");
return;
}

console.log(`\nUpdate available: ${result.currentVersion} → ${result.latestVersion}`);

const installMethod = detectInstallMethod();
const updateCommand = buildUpdateCommand(installMethod);

if (installMethod === "npx") {
console.log(`\nYou are running via npx, which always fetches the latest version.`);
console.log(`Simply restart your command to use the latest version.`);
return;
}

console.log(`\nRunning: ${updateCommand}`);

const { execSync } = await import("node:child_process");
try {
execSync(updateCommand, { stdio: "inherit" });
console.log(`\nSuccessfully updated to ${result.latestVersion}.`);
} catch {
console.error(`\nAutomatic update failed. Run the following command manually:`);
console.error(` ${updateCommand}`);
process.exitCode = 1;
}
});

return program;
}

Expand All @@ -13422,6 +13514,7 @@ export async function runCli(argv: string[] = process.argv): Promise<void> {
cliEvasionEnabled = true;
cliEvasionLevel = undefined;
cliSelectorLocale = undefined;
readUpdateCheckEnabled = (): boolean => true;
process.argv = originalArgv;
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/__tests__/e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ import {
LINKEDIN_PRIVACY_PREPARE_UPDATE_SETTING_TOOL,
LINKEDIN_SEARCH_TOOL,
LINKEDIN_SESSION_HEALTH_TOOL,
LINKEDIN_UPDATE_CHECK_TOOL,
LINKEDIN_SESSION_OPEN_LOGIN_TOOL,
LINKEDIN_SESSION_STATUS_TOOL,
SUBMIT_FEEDBACK_TOOL
Expand Down Expand Up @@ -987,6 +988,7 @@ export async function getCliCoverageFixtures(runtime: CoreRuntime): Promise<CliC

/** Canonical MCP tool names used by the E2E contract suites. */
export const MCP_TOOL_NAMES = {
updateCheck: LINKEDIN_UPDATE_CHECK_TOOL,
analyticsContentMetrics: LINKEDIN_ANALYTICS_CONTENT_METRICS_TOOL,
analyticsPostMetrics: LINKEDIN_ANALYTICS_POST_METRICS_TOOL,
analyticsProfileViews: LINKEDIN_ANALYTICS_PROFILE_VIEWS_TOOL,
Expand Down
Loading