linkedin-apply-assistant 0.1.3 → 0.1.5

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 CHANGED
@@ -6,6 +6,33 @@ This file follows the spirit of Keep a Changelog and uses semantic version label
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.5] - 2026-06-13
10
+
11
+ ### Added
12
+
13
+ - Added `linkedin-apply-assistant update` with automatic npm vs PowerShell
14
+ install-channel detection, plus `--check` and `--method` options.
15
+ - Added PowerShell installer update flags: `install.ps1 -Update` and
16
+ `install.ps1 -CheckOnly`.
17
+
18
+ ### Changed
19
+
20
+ - NPM launcher and PowerShell installer shims now mark their install channel so
21
+ the CLI can pick the correct updater automatically.
22
+ - NPM package contents now include only Python source files from
23
+ `src/linkedin_apply_assistant/*.py`, preventing generated `__pycache__`
24
+ bytecode from entering npm tarballs after test runs.
25
+
26
+ ## [0.1.4] - 2026-06-13
27
+
28
+ ### Fixed
29
+
30
+ - Made `linkedin-apply-assistant --verbose` print root help and a first-run hint
31
+ instead of failing with a missing `command` parser error.
32
+ - Made `linkedin-apply-assistant config` default to the read-only
33
+ `config check` diagnostics command instead of failing with a missing
34
+ `config_command` parser error.
35
+
9
36
  ## [0.1.3] - 2026-06-13
10
37
 
11
38
  ### Changed
package/README.md CHANGED
@@ -7,7 +7,7 @@ LinkedIn-apply-assistant is an experimental local browser automation assistant f
7
7
 
8
8
  The package is local-first. It does not require credentials in config, copied browser profiles, private documents, or generated reports to import, inspect, or run its deterministic tests.
9
9
 
10
- Current package metadata version: `0.1.3`.
10
+ Current package metadata version: `0.1.5`.
11
11
 
12
12
  ## Safety Boundary
13
13
 
@@ -98,6 +98,7 @@ All commands accept these shared flags where relevant: `--workspace`, `--config`
98
98
  | `apply` | Prepare approval-gated application audit output. Browser submission remains disabled today. | `--input`, `--limit`, `--confirm-submit` |
99
99
  | `dry-run` | Validate local job input without browser or network submission. | `--input` |
100
100
  | `report` | Read a local JSON report and print a concise summary. | `report_json` |
101
+ | `update` | Update through the detected npm or PowerShell install channel. | `--check`, `--method` |
101
102
 
102
103
  ## Documentation Map
103
104
 
@@ -443,6 +443,100 @@ Distribution safety boundary:
443
443
  download and run that public script
444
444
  - PyPI and TestPyPI uploads stay out of this release
445
445
 
446
+ ## v0.1.4 CLI Missing-Command UX Release
447
+
448
+ This patch fixes first-run command-line behavior so missing subcommands produce
449
+ actionable output instead of raw argparse parser errors.
450
+
451
+ Scope:
452
+
453
+ - Package version: `0.1.4`
454
+ - npm package: `linkedin-apply-assistant`
455
+ - npm dist-tag: `latest`
456
+ - GitHub Release: `v0.1.4`
457
+ - Repository: `MohammedGhazal09/linkedin-apply-assistant`
458
+ - Runtime behavior, browser safety posture, and public apply/search boundaries are unchanged.
459
+ - `linkedin-apply-assistant --verbose` prints root help and a first-run hint.
460
+ - `linkedin-apply-assistant config` runs the same read-only diagnostics as
461
+ `linkedin-apply-assistant config check`.
462
+ - PyPI and TestPyPI remain future channels.
463
+
464
+ Required local evidence before public sync:
465
+
466
+ ```powershell
467
+ python -m pytest tests\test_cli_help.py tests\test_config_diagnostics.py tests\test_distribution_metadata.py tests\test_docs_smoke.py tests\test_registry_publication_strategy.py tests\test_release_readiness.py tests\test_npm_launcher.py tests\test_distribution_smoke.py tests\test_release_manifest.py -q
468
+ python scripts\release.py clean
469
+ python scripts\release.py manifest --check
470
+ python scripts\release.py verify
471
+ npm pack --dry-run --json
472
+ ```
473
+
474
+ Post-publish verification:
475
+
476
+ ```powershell
477
+ npm view linkedin-apply-assistant version --json
478
+ npm view linkedin-apply-assistant dist-tags --json
479
+ linkedin-apply-assistant --verbose
480
+ linkedin-apply-assistant --verbose config
481
+ gh release view v0.1.4 --repo MohammedGhazal09/linkedin-apply-assistant --json tagName,name,url,isDraft,isPrerelease,targetCommitish
482
+ ```
483
+
484
+ Distribution safety boundary:
485
+
486
+ - no lifecycle install, publish, or token scripts are added to `package.json`
487
+ - `config` shorthand remains read-only and creates no files or directories
488
+ - PyPI and TestPyPI uploads stay out of this release
489
+
490
+ ## v0.1.5 NPM and PowerShell Update Command Release
491
+
492
+ This patch adds an explicit update path for both public install channels.
493
+
494
+ Scope:
495
+
496
+ - Package version: `0.1.5`
497
+ - npm package: `linkedin-apply-assistant`
498
+ - npm dist-tag: `latest`
499
+ - GitHub Release: `v0.1.5`
500
+ - Repository: `MohammedGhazal09/linkedin-apply-assistant`
501
+ - Runtime behavior, browser safety posture, and public apply/search boundaries are unchanged.
502
+ - `linkedin-apply-assistant update` chooses npm or PowerShell from the detected
503
+ install channel.
504
+ - `linkedin-apply-assistant update --check` prints the selected updater without
505
+ changing files.
506
+ - `install.ps1 -Update` reruns the no-admin installer against the same install
507
+ directory when the PowerShell shim provides it.
508
+ - `install.ps1 -CheckOnly` prints installer settings without changing files.
509
+ - PyPI and TestPyPI remain future channels.
510
+
511
+ Required local evidence before public sync:
512
+
513
+ ```powershell
514
+ python -m pytest tests\test_cli_help.py tests\test_cli_contract.py tests\test_config_diagnostics.py tests\test_distribution_metadata.py tests\test_docs_smoke.py tests\test_registry_publication_strategy.py tests\test_release_readiness.py tests\test_npm_launcher.py tests\test_distribution_smoke.py tests\test_release_manifest.py -q
515
+ python scripts\release.py clean
516
+ python scripts\release.py manifest --check
517
+ python scripts\release.py verify
518
+ npm pack --dry-run --json
519
+ powershell -NoProfile -Command "$errors=$null; [System.Management.Automation.PSParser]::Tokenize((Get-Content -Raw .\install.ps1), [ref]$errors) | Out-Null; if($errors){$errors; exit 1}"
520
+ linkedin-apply-assistant update --check
521
+ ```
522
+
523
+ Post-publish verification:
524
+
525
+ ```powershell
526
+ npm view linkedin-apply-assistant version --json
527
+ npm view linkedin-apply-assistant dist-tags --json
528
+ linkedin-apply-assistant update --check
529
+ gh release view v0.1.5 --repo MohammedGhazal09/linkedin-apply-assistant --json tagName,name,url,isDraft,isPrerelease,targetCommitish
530
+ ```
531
+
532
+ Distribution safety boundary:
533
+
534
+ - no lifecycle install, publish, or token scripts are added to `package.json`
535
+ - `update` runs only the selected package updater and does not touch browser
536
+ profiles, configs, Q&A banks, outputs, or reports
537
+ - browser submission remains disabled
538
+ - PyPI and TestPyPI uploads stay out of this release
539
+
446
540
  ## Required Public Metadata
447
541
 
448
542
  `package.json` must include exactly these public project fields:
@@ -496,7 +590,7 @@ Do not publish while any hard blocker remains unresolved.
496
590
  - `LEGAL.md` and `SAFETY.md` remain linked from README.
497
591
  - `MIGRATION.md` explains extraction scope and excluded root surfaces.
498
592
  - `CONTRIBUTING.md` and `SECURITY.md` are standalone-scoped.
499
- - Changelog has `Unreleased`, `0.1.3`, `0.1.2`, `0.1.1`, and `0.1.0`.
593
+ - Changelog has `Unreleased`, `0.1.5`, `0.1.4`, `0.1.3`, `0.1.2`, `0.1.1`, and `0.1.0`.
500
594
  - Source, Python, npm launcher, and PowerShell installer docs are current and tested.
501
595
  - Phase 21 terminal UX docs and help stay current: `docs\commands.md`, `tests\test_cli_help.py`, and `tests\test_config_diagnostics.py`.
502
596
  - Public package metadata points to the canonical GitHub repository and issue tracker.
package/SECURITY.md CHANGED
@@ -34,4 +34,4 @@ Never attach browser profiles, cookies, credentials, screenshots, private docume
34
34
 
35
35
  ## Supported Versions
36
36
 
37
- The initial standalone GitHub source release is `0.1.0`. Current package metadata is `0.1.3`. Security guidance applies to the current unreleased, `0.1.3`, `0.1.2`, `0.1.1`, and `0.1.0` package-local surfaces.
37
+ The initial standalone GitHub source release is `0.1.0`. Current package metadata is `0.1.5`. Security guidance applies to the current unreleased, `0.1.5`, `0.1.4`, `0.1.3`, `0.1.2`, `0.1.1`, and `0.1.0` package-local surfaces.
@@ -29,6 +29,9 @@ if (existsSync(localSrc)) {
29
29
  : localSrc;
30
30
  }
31
31
 
32
+ launcherEnv.LINKEDIN_APPLY_ASSISTANT_INSTALL_CHANNEL = "npm";
33
+ launcherEnv.LINKEDIN_APPLY_ASSISTANT_NPM_PACKAGE_ROOT = packageRoot;
34
+
32
35
  function printSetupGuidance(reason) {
33
36
  console.error(`linkedin-apply-assistant npm launcher could not start: ${reason}`);
34
37
  console.error("");
package/docs/commands.md CHANGED
@@ -11,6 +11,9 @@ Use this page after installation to choose the right terminal command, inspect f
11
11
  linkedin-apply-assistant config check
12
12
  ```
13
13
 
14
+ `linkedin-apply-assistant config` is accepted as the same read-only
15
+ diagnostic shortcut.
16
+
14
17
  3. Copy example files into your own ignored workspace when you need them:
15
18
  - `configs/config.example.yml` for profile, documents, and path choices.
16
19
  - `configs/qa_bank.example.yml` for truthful reusable answers.
@@ -56,6 +59,12 @@ Run diagnostics before browser workflows or after changing path flags:
56
59
  linkedin-apply-assistant config check
57
60
  ```
58
61
 
62
+ Shortcut:
63
+
64
+ ```powershell
65
+ linkedin-apply-assistant config
66
+ ```
67
+
59
68
  Expected output:
60
69
 
61
70
  - `ok`, `missing`, or `warning` for each path category.
@@ -162,6 +171,36 @@ linkedin-apply-assistant report examples/reports/apply-audit.example.json
162
171
 
163
172
  Use this command for local report review without opening a browser.
164
173
 
174
+ ## update
175
+
176
+ `update` refreshes the installed command through the detected install channel.
177
+ NPM-installed commands run npm; PowerShell-installed commands rerun the public
178
+ installer.
179
+
180
+ ```powershell
181
+ linkedin-apply-assistant update
182
+ ```
183
+
184
+ Preview the selected updater without changing files:
185
+
186
+ ```powershell
187
+ linkedin-apply-assistant update --check
188
+ ```
189
+
190
+ Force a channel when detection is not what you want:
191
+
192
+ ```powershell
193
+ linkedin-apply-assistant update --method npm
194
+ linkedin-apply-assistant update --method powershell
195
+ ```
196
+
197
+ Notes:
198
+
199
+ - The npm method runs `npm install -g linkedin-apply-assistant@latest`.
200
+ - The PowerShell method downloads `install.ps1` and runs it with `-Update`.
201
+ - This command only updates the local package install. It does not submit
202
+ applications or change the browser safety boundary.
203
+
165
204
  ## Troubleshooting Pointers
166
205
 
167
206
  | Symptom | Next step |
@@ -171,6 +210,7 @@ Use this command for local report review without opening a browser.
171
210
  | Invalid JSON input | Re-run `dry-run` after checking the `--input` path and JSON format. |
172
211
  | Missing Playwright or Chromium | Run `python -m playwright install chromium`. |
173
212
  | Browser profile issue | Run `config check`, inspect the browser profile path, or choose another profile with `--browser-profile <path>`. |
213
+ | Unsure how to update | Run `linkedin-apply-assistant update --check`. |
174
214
  | Unsure which command to use | Start with `linkedin-apply-assistant --help` and `linkedin-apply-assistant config check`. |
175
215
 
176
216
  More details are in [Troubleshooting](troubleshooting.md).
@@ -4,7 +4,7 @@ This package runs locally. You install the Python package, choose a local worksp
4
4
 
5
5
  This file is the canonical install matrix. The README keeps only a short quick start.
6
6
 
7
- Current package metadata version: `0.1.3`.
7
+ Current package metadata version: `0.1.5`.
8
8
 
9
9
  The npm launcher and PowerShell no-admin installer are the current quick-install paths.
10
10
  PyPI remains a future package channel. The package-channel decision, approval
@@ -55,6 +55,19 @@ npm install -g linkedin-apply-assistant
55
55
  linkedin-apply-assistant --help
56
56
  ```
57
57
 
58
+ Update:
59
+
60
+ ```powershell
61
+ linkedin-apply-assistant update
62
+ linkedin-apply-assistant update --check
63
+ ```
64
+
65
+ Direct npm equivalent:
66
+
67
+ ```powershell
68
+ npm install -g linkedin-apply-assistant@latest
69
+ ```
70
+
58
71
  If the launcher reports that the Python package is not importable, install the
59
72
  bundled Python package from the global npm package directory:
60
73
 
@@ -75,6 +88,13 @@ does not require admin rights.
75
88
  irm https://raw.githubusercontent.com/MohammedGhazal09/linkedin-apply-assistant/main/install.ps1 | iex
76
89
  ```
77
90
 
91
+ Update:
92
+
93
+ ```powershell
94
+ linkedin-apply-assistant update
95
+ linkedin-apply-assistant update --check
96
+ ```
97
+
78
98
  Inspectable temp-file equivalent:
79
99
 
80
100
  ```powershell
@@ -83,6 +103,20 @@ iwr https://raw.githubusercontent.com/MohammedGhazal09/linkedin-apply-assistant/
83
103
  & $script
84
104
  ```
85
105
 
106
+ Inspectable update equivalent:
107
+
108
+ ```powershell
109
+ $script = Join-Path $env:TEMP 'linkedin-apply-assistant-install.ps1'
110
+ iwr https://raw.githubusercontent.com/MohammedGhazal09/linkedin-apply-assistant/main/install.ps1 -OutFile $script
111
+ & $script -Update
112
+ ```
113
+
114
+ Check installer settings without changing files:
115
+
116
+ ```powershell
117
+ & $script -CheckOnly
118
+ ```
119
+
86
120
  Optional visible-browser setup during install:
87
121
 
88
122
  ```powershell
@@ -7,17 +7,17 @@ asset, or grant publish-capable workflow permissions.
7
7
 
8
8
  The first GitHub source release, `v0.1.0`, remains source-only and is not a
9
9
  registry backfill candidate. The npm launcher release starts at `0.1.1`; the
10
- current PowerShell short-command npm package page refresh is `0.1.3`. PyPI and
11
- TestPyPI remain future channels.
10
+ current npm and PowerShell update command release is `0.1.5`. PyPI and TestPyPI
11
+ remain future channels.
12
12
 
13
13
  ## Current Boundary
14
14
 
15
- - Current package metadata version: `0.1.3`.
15
+ - Current package metadata version: `0.1.5`.
16
16
  - Current install path: npm global launcher, PowerShell installer, source
17
17
  checkout, local Python install, local editable install, and local npm launcher
18
18
  dry-run validation.
19
19
  - Current public channel: GitHub repository source checkout and GitHub source
20
- release archives; npm launcher package for `0.1.3` after the approved npm
20
+ release archives; npm launcher package for `0.1.5` after the approved npm
21
21
  patch publish step verifies successfully.
22
22
  - Not current: PyPI package, TestPyPI package, GitHub Packages package, PyPI
23
23
  trusted-publisher setup, npm trusted-publisher setup, registry automation,
@@ -34,14 +34,14 @@ action, and exact mutation.
34
34
  | GitHub Releases | Current source-only channel for `v0.1.0`. No wheel, sdist, npm tarball, or other release asset is attached. | Keep as the source-of-truth release record. Future assets require explicit approval. | Users can inspect and install from source without introducing registry auth or package-name ownership. | Clean local verification, changelog, release checklist, source manifest, approved tag or release mutation. | Explicit GitHub Release approval naming repo, tag, target commit, release state, and assets, if any. | `gh release view`, `gh release list`, source archive inspection, release manifest verification. | Remove mistaken assets from the release, correct the release notes, or delete a draft. Source tags need separate explicit remediation because asset removal does not undo a tag. |
35
35
  | PyPI | Not published and not reserved. | Primary future Python registry for direct package publication. | The project is a Python CLI with Playwright-driven browser automation, so PyPI is the natural long-term install path. | Maintainer or maintainer-controlled organization ownership, account 2FA where supported, PyPI Trusted Publishing with GitHub Actions OIDC, protected `pypi` environment, clean build and metadata gates. | Explicit PyPI approval naming repository, version, PyPI project, workflow or manual action, and exact upload mutation. | Read-only JSON API check, `python -m build`, `twine check dist/*`, local wheel install smoke, release scan, manifest verification. | Prefer yanking a broken release where appropriate. Deletion is disruptive and permanent; never rely on deleting and reusing the same version. |
36
36
  | TestPyPI | Not published and not reserved. | Required preflight for the first registry release and for publish-workflow changes. Routine patch preflights can become optional only after a proven release cycle. | It exercises package metadata, artifacts, and installer behavior before the real PyPI release. | Same artifact gates as PyPI, protected `testpypi` environment, explicit preflight approval, no production token. | Explicit TestPyPI approval naming repository, version, TestPyPI project, workflow or manual action, and exact upload mutation. | TestPyPI JSON API check, metadata validation, test-index install smoke, package contents review. | Clean up mistaken TestPyPI releases where possible and move forward with a new version if needed. Do not treat TestPyPI cleanup as production rollback proof. |
37
- | npm | Public thin-launcher channel for `0.1.3`; `0.1.1` was the first npm and PowerShell distribution release. | Keep as the JavaScript ecosystem convenience launcher; use PyPI later for direct Python package installs. | npm provides a familiar global command on systems that already have Node.js, but the launcher delegates to the Python CLI and cannot install Python itself. | Maintainer or maintainer-controlled ownership, account 2FA where supported, first-publish token bootstrap if trusted publishing cannot create the package, exact package contents review. | Explicit npm approval naming repository, version, npm package, workflow or manual action, and exact registry mutation. | `npm pack --dry-run --json`, package contents inspection, no lifecycle install/publish scripts, npm read-only registry check after publication. | Prefer deprecation for bad packages when unpublish criteria are not met. A used npm `package@version` cannot be reused, even after unpublish. |
37
+ | npm | Public thin-launcher channel for `0.1.5`; `0.1.1` was the first npm and PowerShell distribution release. | Keep as the JavaScript ecosystem convenience launcher; use PyPI later for direct Python package installs. | npm provides a familiar global command on systems that already have Node.js, but the launcher delegates to the Python CLI and cannot install Python itself. | Maintainer or maintainer-controlled ownership, account 2FA where supported, first-publish token bootstrap if trusted publishing cannot create the package, exact package contents review. | Explicit npm approval naming repository, version, npm package, workflow or manual action, and exact registry mutation. | `npm pack --dry-run --json`, package contents inspection, no lifecycle install/publish scripts, npm read-only registry check after publication. | Prefer deprecation for bad packages when unpublish criteria are not met. A used npm `package@version` cannot be reused, even after unpublish. |
38
38
  | PowerShell installer | Current GitHub-hosted installer script at `install.ps1`; the README uses `irm ... \| iex` and the detailed install doc keeps a temp-file equivalent. | Keep as the no-admin Windows convenience path for users who prefer a single script. | It can create a local virtual environment, install dependencies from the public source archive, create command shims, and optionally install Chromium. | Public GitHub source archive availability, Python 3.11+, script syntax validation, and local install smoke. | Push the verified installer/docs to the public repository; no registry mutation is required for installer-only changes. | PowerShell parser check, temp-directory install smoke, command shim help smoke, and docs link checks. | Fix forward in `main`; users can reinstall from the corrected script or pin `-Ref` to a known tag. |
39
39
  | GitHub Packages | Not used. | Deferred. | It adds `packages: write` and authenticated consumption friction without improving the primary Python install path. | A future reason to publish a package to GitHub Packages, explicit package type, permission model, and install documentation. | Separate approval naming package type, repository, version, and exact mutation. | GitHub Packages read-only checks and package permission review. | Delete or deprecate only according to the package type's GitHub Packages support. This is not a substitute for PyPI/npm remediation. |
40
40
 
41
41
  ## Package Names
42
42
 
43
43
  The target package name for PyPI, TestPyPI, and npm publication is
44
- `linkedin-apply-assistant`. The current npm launcher version is `0.1.3`.
44
+ `linkedin-apply-assistant`. The current npm launcher version is `0.1.5`.
45
45
 
46
46
  If the unscoped npm name becomes unavailable or ownership changes later, the
47
47
  fallback is a future scoped npm package under a maintainer-controlled scope.
@@ -58,6 +58,11 @@ fallback is a future scoped npm package under a maintainer-controlled scope.
58
58
  README content is immutable for an existing package version.
59
59
  - The PowerShell short-command README refresh uses `0.1.3` because the npm
60
60
  package page must be republished to replace the prior command.
61
+ - The CLI no-command and `config` shorthand fix uses `0.1.4` because it changes
62
+ user-facing terminal behavior without changing browser workflow behavior.
63
+ - The npm and PowerShell update command release uses `0.1.5` because it adds a
64
+ user-facing updater and installer flags without changing browser workflow
65
+ behavior.
61
66
  - If user-visible behavior changes before registry publication, the default
62
67
  future version example is `0.2.0`.
63
68
  - Future behavior changes remain SemVer decisions at the publish phase.
package/install.ps1 CHANGED
@@ -3,7 +3,9 @@ param(
3
3
  [string]$InstallDir = (Join-Path $env:LOCALAPPDATA "linkedin-apply-assistant"),
4
4
  [string]$Ref = "main",
5
5
  [switch]$InstallBrowser,
6
- [switch]$NoPath
6
+ [switch]$NoPath,
7
+ [switch]$Update,
8
+ [switch]$CheckOnly
7
9
  )
8
10
 
9
11
  Set-StrictMode -Version 3.0
@@ -103,8 +105,19 @@ $binDir = Join-Path $installRoot "bin"
103
105
  $tempDir = Join-Path ([IO.Path]::GetTempPath()) ("linkedin-apply-assistant-" + [guid]::NewGuid())
104
106
  $zipPath = Join-Path $tempDir "source.zip"
105
107
 
108
+ if ($CheckOnly) {
109
+ Write-Step "Installer source: $archiveUrl"
110
+ Write-Step "Install directory: $installRoot"
111
+ Write-Step "Ref: $Ref"
112
+ Write-Host ""
113
+ Write-Host "Run update:"
114
+ Write-Host " powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -Update"
115
+ exit 0
116
+ }
117
+
106
118
  try {
107
- Write-Step "Installing from $archiveUrl"
119
+ $action = if ($Update) { "Updating" } else { "Installing" }
120
+ Write-Step "$action from $archiveUrl"
108
121
  New-Item -ItemType Directory -Force -Path $tempDir | Out-Null
109
122
  New-Item -ItemType Directory -Force -Path $installRoot | Out-Null
110
123
 
@@ -147,13 +160,22 @@ try {
147
160
  $psShim = Join-Path $binDir "linkedin-apply-assistant.ps1"
148
161
  $cmdShim = Join-Path $binDir "linkedin-apply-assistant.cmd"
149
162
 
150
- @"
163
+ @"
151
164
  param([Parameter(ValueFromRemainingArguments = `$true)][string[]]`$RemainingArgs)
165
+ `$env:LINKEDIN_APPLY_ASSISTANT_INSTALL_CHANNEL = "powershell"
166
+ `$env:LINKEDIN_APPLY_ASSISTANT_INSTALL_DIR = "$installRoot"
167
+ `$env:LINKEDIN_APPLY_ASSISTANT_INSTALL_REF = "$Ref"
152
168
  & "$venvPython" -m linkedin_apply_assistant.cli @RemainingArgs
153
169
  exit `$LASTEXITCODE
154
170
  "@ | Set-Content -LiteralPath $psShim -Encoding UTF8
155
171
 
156
- "@echo off`r`n`"$venvPython`" -m linkedin_apply_assistant.cli %*`r`n" |
172
+ @"
173
+ @echo off
174
+ set "LINKEDIN_APPLY_ASSISTANT_INSTALL_CHANNEL=powershell"
175
+ set "LINKEDIN_APPLY_ASSISTANT_INSTALL_DIR=$installRoot"
176
+ set "LINKEDIN_APPLY_ASSISTANT_INSTALL_REF=$Ref"
177
+ "$venvPython" -m linkedin_apply_assistant.cli %*
178
+ "@ |
157
179
  Set-Content -LiteralPath $cmdShim -Encoding ASCII
158
180
 
159
181
  if (-not $NoPath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linkedin-apply-assistant",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Thin npm launcher for the Python LinkedIn apply assistant package",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -18,7 +18,7 @@
18
18
  "files": [
19
19
  "bin/linkedin-apply-assistant.mjs",
20
20
  "pyproject.toml",
21
- "src/",
21
+ "src/linkedin_apply_assistant/*.py",
22
22
  "install.ps1",
23
23
  "README.md",
24
24
  "SAFETY.md",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "linkedin-apply-assistant"
7
- version = "0.1.3"
7
+ version = "0.1.5"
8
8
  description = "Local LinkedIn application assistant with user-visible browser workflows"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  """Package identity for the standalone LinkedIn application assistant."""
2
2
 
3
- __version__ = "0.1.3"
3
+ __version__ = "0.1.5"
4
4
 
5
5
  APP_DISPLAY_NAME = "LinkedIn-apply-assistant"
6
6
  APP_PACKAGE_NAME = "linkedin-apply-assistant"
@@ -4,10 +4,14 @@ from __future__ import annotations
4
4
 
5
5
  import argparse
6
6
  import json
7
+ import os
8
+ import shutil
9
+ import subprocess
7
10
  import sys
8
11
  from pathlib import Path
9
12
  from typing import Any
10
13
 
14
+ from . import APP_PACKAGE_NAME, __version__
11
15
  from .apply_reports import RuntimeReportSink
12
16
  from .ats_handlers import DisabledSubmissionPolicy
13
17
  from .browser_sessions import PLAYWRIGHT_CHROMIUM_INSTALL_COMMAND, VisibleBrowserSessionFactory
@@ -29,6 +33,11 @@ REQUIRED_JOB_FIELDS = ("title", "company", "url", "location", "description")
29
33
  CONFIG_CHECK_COMMAND = "linkedin-apply-assistant config check"
30
34
  CONFIG_EXAMPLE_PATH = "configs/config.example.yml"
31
35
  QA_BANK_EXAMPLE_PATH = "configs/qa_bank.example.yml"
36
+ PUBLIC_INSTALLER_URL = (
37
+ "https://raw.githubusercontent.com/MohammedGhazal09/linkedin-apply-assistant/main/install.ps1"
38
+ )
39
+ INSTALL_CHANNEL_ENV = "LINKEDIN_APPLY_ASSISTANT_INSTALL_CHANNEL"
40
+ INSTALL_DIR_ENV = "LINKEDIN_APPLY_ASSISTANT_INSTALL_DIR"
32
41
  NO_SUBMIT_HELP = (
33
42
  "Safety: public workflows are no-submit by default; assist is fill-only and "
34
43
  "browser submission remains disabled in apply."
@@ -108,7 +117,12 @@ Outputs use the resolved output directory and reports are written under its repo
108
117
  {NO_SUBMIT_HELP}
109
118
  """,
110
119
  )
111
- subparsers = parser.add_subparsers(dest="command", required=True)
120
+ parser.add_argument(
121
+ "--version",
122
+ action="version",
123
+ version=f"%(prog)s {__version__}",
124
+ )
125
+ subparsers = parser.add_subparsers(dest="command")
112
126
 
113
127
  config = subparsers.add_parser(
114
128
  "config",
@@ -124,7 +138,7 @@ The diagnostic resolves config, Q&A bank, browser profile, output, reports, data
124
138
  It creates no files and no directories.
125
139
  """,
126
140
  )
127
- config_subparsers = config.add_subparsers(dest="config_command", required=True)
141
+ config_subparsers = config.add_subparsers(dest="config_command")
128
142
  config_check = config_subparsers.add_parser(
129
143
  "check",
130
144
  parents=[subcommand_common],
@@ -246,6 +260,41 @@ This command is browser-free and reads an existing local report file.
246
260
  report.add_argument("report_json", help="Path to the report JSON file.")
247
261
  report.set_defaults(handler=_handle_report)
248
262
 
263
+ update = subparsers.add_parser(
264
+ "update",
265
+ parents=[subcommand_common],
266
+ formatter_class=formatter,
267
+ help="Update the installed package through npm or the PowerShell installer.",
268
+ description=(
269
+ "Update linkedin-apply-assistant through the detected install channel. "
270
+ "npm installs use npm; PowerShell installs rerun the public installer."
271
+ ),
272
+ epilog=f"""Examples:
273
+ linkedin-apply-assistant update
274
+ linkedin-apply-assistant update --check
275
+ linkedin-apply-assistant update --method npm
276
+ linkedin-apply-assistant update --method powershell
277
+
278
+ NPM update command:
279
+ npm install -g {APP_PACKAGE_NAME}@latest
280
+
281
+ PowerShell update command:
282
+ irm {PUBLIC_INSTALLER_URL} | iex
283
+ """,
284
+ )
285
+ update.add_argument(
286
+ "--method",
287
+ choices=("auto", "npm", "powershell"),
288
+ default="auto",
289
+ help="Choose the update mechanism. Auto uses the detected install channel.",
290
+ )
291
+ update.add_argument(
292
+ "--check",
293
+ action="store_true",
294
+ help="Show the selected update command without running it.",
295
+ )
296
+ update.set_defaults(handler=_handle_update)
297
+
249
298
  return parser
250
299
 
251
300
 
@@ -555,9 +604,107 @@ def _handle_report(args: argparse.Namespace) -> int:
555
604
  return 0
556
605
 
557
606
 
607
+ def _detect_update_method(requested: str) -> str:
608
+ if requested != "auto":
609
+ return requested
610
+
611
+ channel = os.environ.get(INSTALL_CHANNEL_ENV, "").strip().lower()
612
+ if channel in {"npm", "powershell"}:
613
+ return channel
614
+
615
+ npm = shutil.which("npm")
616
+ if npm:
617
+ try:
618
+ result = subprocess.run(
619
+ [npm, "root", "-g"],
620
+ text=True,
621
+ capture_output=True,
622
+ check=False,
623
+ timeout=10,
624
+ )
625
+ except (OSError, subprocess.SubprocessError):
626
+ result = None
627
+ if result and result.returncode == 0:
628
+ package_root = Path(result.stdout.strip()) / APP_PACKAGE_NAME / "package.json"
629
+ if package_root.exists():
630
+ return "npm"
631
+
632
+ if os.name == "nt":
633
+ return "powershell"
634
+ return "npm"
635
+
636
+
637
+ def _powershell_quote(value: str) -> str:
638
+ return "'" + value.replace("'", "''") + "'"
639
+
640
+
641
+ def _powershell_update_command_parts() -> tuple[list[str], str]:
642
+ powershell = shutil.which("pwsh") or shutil.which("powershell")
643
+ if not powershell:
644
+ return [], f"irm {PUBLIC_INSTALLER_URL} | iex"
645
+
646
+ install_dir = os.environ.get(INSTALL_DIR_ENV, "").strip()
647
+ command = (
648
+ "$script = Join-Path $env:TEMP 'linkedin-apply-assistant-install.ps1'; "
649
+ f"iwr {PUBLIC_INSTALLER_URL} -OutFile $script; "
650
+ "& $script -Update"
651
+ )
652
+ if install_dir:
653
+ command = f"{command} -InstallDir {_powershell_quote(install_dir)}"
654
+ return [powershell, "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command], command
655
+
656
+
657
+ def _npm_update_command_parts() -> tuple[list[str], str]:
658
+ npm = shutil.which("npm")
659
+ command = ["npm", "install", "-g", f"{APP_PACKAGE_NAME}@latest"]
660
+ display = " ".join(command)
661
+ return ([npm, *command[1:]] if npm else [], display)
662
+
663
+
664
+ def _handle_update(args: argparse.Namespace) -> int:
665
+ method = _detect_update_method(args.method)
666
+ if method == "npm":
667
+ command, display = _npm_update_command_parts()
668
+ else:
669
+ command, display = _powershell_update_command_parts()
670
+
671
+ print(f"Current version: {__version__}")
672
+ print(f"Update method: {method}")
673
+ print(f"Update command: {display}")
674
+
675
+ if args.check:
676
+ return 0
677
+
678
+ if not command:
679
+ _print_error(
680
+ f"{method} updater is not available on PATH.",
681
+ f"Run manually: {display}",
682
+ )
683
+ return 2
684
+
685
+ result = subprocess.run(command, check=False)
686
+ if result.returncode != 0:
687
+ _print_error(
688
+ f"Update command failed with exit code {result.returncode}.",
689
+ f"Run manually: {display}",
690
+ )
691
+ return int(result.returncode or 1)
692
+
693
+ print("Update complete.")
694
+ return 0
695
+
696
+
558
697
  def main(argv: list[str] | None = None) -> int:
559
698
  parser = build_parser()
560
699
  args = parser.parse_args(argv)
700
+ if args.command is None:
701
+ parser.print_help()
702
+ print()
703
+ print(f"Try: {CONFIG_CHECK_COMMAND}")
704
+ return 0
705
+ if args.command == "config" and args.config_command is None:
706
+ args.config_command = "check"
707
+ args.handler = _handle_config_check
561
708
  handler = getattr(args, "handler")
562
709
  try:
563
710
  return int(handler(args))