@codyswann/lisa 2.338.1 → 2.338.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +5 -3
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/SKILL.md +22 -5
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +15 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/scripts/workstation.mjs +84 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/SKILL.md +22 -5
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +15 -0
- package/plugins/lisa/skills/lisa-setup-workstation/scripts/workstation.mjs +84 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/SKILL.md +22 -5
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +15 -0
- package/plugins/lisa-agy/skills/lisa-setup-workstation/scripts/workstation.mjs +84 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/SKILL.md +22 -5
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +15 -0
- package/plugins/lisa-copilot/skills/lisa-setup-workstation/scripts/workstation.mjs +84 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/SKILL.md +22 -5
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +15 -0
- package/plugins/lisa-cursor/skills/lisa-setup-workstation/scripts/workstation.mjs +84 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/skills/lisa-setup-remote-env/SKILL.md +22 -5
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +15 -0
- package/plugins/src/base/skills/lisa-setup-workstation/scripts/workstation.mjs +84 -0
|
@@ -12,11 +12,26 @@ Prepare a remote surface so a host project can execute there. Today that means *
|
|
|
12
12
|
|
|
13
13
|
The remote environment's own configuration fields stay **one line into the repository**. Nothing else is pasted into a vendor UI.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
**Ask for that line; do not copy it from here.**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx -y @codyswann/lisa@latest remote-env --emit=claude-web
|
|
18
19
|
```
|
|
19
20
|
|
|
21
|
+
The field pins the exact Lisa it runs, and the emitter reads that version from
|
|
22
|
+
the package it is running out of — so the line it prints is always pinned to the
|
|
23
|
+
version that printed it.
|
|
24
|
+
|
|
25
|
+
A copy written into this file cannot have that property. It is a snapshot taken
|
|
26
|
+
when the package was *built*, and the release bumps the version *after* the
|
|
27
|
+
build, so a published tarball's copy names the previous release. That is not a
|
|
28
|
+
cosmetic lag: the pinned version predates whatever this file documents, so a
|
|
29
|
+
pasted copy can call flags that version does not implement. Measured on
|
|
30
|
+
`2.338.1`, whose embedded copy named `2.338.0` — a Lisa with no `--print-tools`,
|
|
31
|
+
no `--tools`, and no `tools-from-notes.mjs`. Unknown flags are ignored rather
|
|
32
|
+
than rejected, so it did not fail; it installed the full catalogue and re-ran the
|
|
33
|
+
whole setup, three times over, inside a five-minute budget.
|
|
34
|
+
|
|
20
35
|
**That line is identical for every project AND every surface.** Nothing in it names the
|
|
21
36
|
repository or the package manager, so it can be pasted unchanged anywhere.
|
|
22
37
|
|
|
@@ -236,8 +251,10 @@ When emitting, produce exactly:
|
|
|
236
251
|
```text
|
|
237
252
|
Environment name: <project> remote executor
|
|
238
253
|
Repository: <org>/<repo> (must be the default checkout)
|
|
239
|
-
Setup script:
|
|
240
|
-
|
|
254
|
+
Setup script: <printed by `remote-env --emit`; never copied
|
|
255
|
+
from prose, so its pin is always the version
|
|
256
|
+
that printed it>
|
|
257
|
+
Maintenance: <same line, printed by the same command>
|
|
241
258
|
(identical for every project — the script finds the
|
|
242
259
|
checkout and installs from the committed lockfile)
|
|
243
260
|
Environment vars: LISA_SECRETS_SURFACE=codex-cloud
|
|
@@ -912,6 +912,21 @@ export function emitClaudeWeb({ bootstrapKey }) {
|
|
|
912
912
|
" is why exactly one value needs to live here.",
|
|
913
913
|
"",
|
|
914
914
|
" Setup script:",
|
|
915
|
+
"",
|
|
916
|
+
" A setup script sees NONE of the variables configured above — it runs",
|
|
917
|
+
" while the image is being built, before the environment exists. So a",
|
|
918
|
+
" session with no checkout has to be told its tenant inside the script",
|
|
919
|
+
" itself, or the field finds nothing to prepare and exits 0 looking like",
|
|
920
|
+
" success. Put these three lines FIRST, then the line after them:",
|
|
921
|
+
"",
|
|
922
|
+
" export LISA_TENANT=<your namespace>",
|
|
923
|
+
` export ${key}='<the same value as above>'`,
|
|
924
|
+
" export LISA_SECRETS_SURFACE=claude-web",
|
|
925
|
+
"",
|
|
926
|
+
" LISA_SECRETS_SURFACE is set explicitly rather than left to detection:",
|
|
927
|
+
" detection keys off CLAUDE_CODE_REMOTE, and a surface that does not set",
|
|
928
|
+
" it falls through to `local`, which writes no credentials to disk at all.",
|
|
929
|
+
"",
|
|
915
930
|
` ${SETUP_FIELD}`,
|
|
916
931
|
"",
|
|
917
932
|
" This line is identical for every project and every surface — nothing",
|
|
@@ -34,6 +34,8 @@
|
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
import { execFileSync } from "node:child_process";
|
|
37
|
+
import { existsSync, lstatSync, mkdirSync, rmSync, symlinkSync } from "node:fs";
|
|
38
|
+
import { join } from "node:path";
|
|
37
39
|
|
|
38
40
|
import {
|
|
39
41
|
AGENTS,
|
|
@@ -297,6 +299,84 @@ export function renderPlan(plan) {
|
|
|
297
299
|
* @param {object} [deps] Injected runner, for tests.
|
|
298
300
|
* @returns {object} Result row.
|
|
299
301
|
*/
|
|
302
|
+
/**
|
|
303
|
+
* Make a tool that installed elsewhere reachable from `~/.local/bin`.
|
|
304
|
+
*
|
|
305
|
+
* A vendor script installs where it likes — SonarQube's puts `sonar` under
|
|
306
|
+
* `~/.local/share/sonarqube-cli/bin` — and the catalogue records that in
|
|
307
|
+
* `binDir`. Until now the only thing that put those directories on PATH was an
|
|
308
|
+
* edit to the shell rc files, which a cloud container never reads: its tool
|
|
309
|
+
* shell is not a login shell. So the tool installed, and the session that asked
|
|
310
|
+
* for it could not find it. Observed on a live Claude Tag channel, where
|
|
311
|
+
* `~/.local/share/sonarqube-cli` existed and `command -v sonar` came back empty.
|
|
312
|
+
*
|
|
313
|
+
* Exporting PATH from the setup script does not fix that either — those exports
|
|
314
|
+
* die with the process that ran them. Only the filesystem survives into the
|
|
315
|
+
* session, so the fix has to be a file.
|
|
316
|
+
*
|
|
317
|
+
* A symlink into `~/.local/bin` is the shape the AWS installer already uses
|
|
318
|
+
* (`aws -> ~/.local/aws-cli/v2/current/bin/aws`), and that directory is the one
|
|
319
|
+
* place every surface agrees is on PATH.
|
|
320
|
+
*
|
|
321
|
+
* Deliberately never fatal. A tool that is already reachable does not need the
|
|
322
|
+
* link, and one that cannot be linked is caught a moment later by the probe —
|
|
323
|
+
* which reports the real problem instead of a symlink error standing in for it.
|
|
324
|
+
* @param {object} entry Catalogue entry just installed.
|
|
325
|
+
* @param {object} [deps] Injected seams, for tests.
|
|
326
|
+
*/
|
|
327
|
+
export function linkIntoBinDir(entry, deps = {}) {
|
|
328
|
+
if (!entry.binDir) return;
|
|
329
|
+
|
|
330
|
+
const home = deps.home ?? process.env.HOME ?? "";
|
|
331
|
+
const link = deps.link ?? symlinkSync;
|
|
332
|
+
const exists = deps.exists ?? existsSync;
|
|
333
|
+
const remove = deps.remove ?? rmSync;
|
|
334
|
+
const makeDir = deps.mkdir ?? mkdirSync;
|
|
335
|
+
// Two different questions, and they need two different probes.
|
|
336
|
+
//
|
|
337
|
+
// The SOURCE must be followed: a link pointing at a binary that is not there
|
|
338
|
+
// means the install did not produce one, whatever the link says.
|
|
339
|
+
//
|
|
340
|
+
// The TARGET must NOT be followed. `existsSync` reports false for a dangling
|
|
341
|
+
// symlink, which is precisely the case this function claims to repair — the
|
|
342
|
+
// vendor moved and the old link now points at nothing. Following it would
|
|
343
|
+
// skip the removal, `symlinkSync` would throw EEXIST, the catch below would
|
|
344
|
+
// swallow it, and the broken link would survive the fix meant to replace it.
|
|
345
|
+
const present = deps.present ?? (path => lstatPresent(path));
|
|
346
|
+
|
|
347
|
+
const source = join(
|
|
348
|
+
entry.binDir.replace(/^~/, home),
|
|
349
|
+
entry.binary ?? entry.name
|
|
350
|
+
);
|
|
351
|
+
const target = join(home, ".local", "bin", entry.name);
|
|
352
|
+
if (source === target || !exists(source)) return;
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
makeDir(join(home, ".local", "bin"), { recursive: true });
|
|
356
|
+
// Replaced rather than skipped-if-present: a stale link left by an earlier
|
|
357
|
+
// version pointing at a path the vendor has since moved is worse than none,
|
|
358
|
+
// because it resolves and then fails at the moment of use.
|
|
359
|
+
if (present(target)) remove(target, { force: true });
|
|
360
|
+
link(source, target);
|
|
361
|
+
} catch {
|
|
362
|
+
// See above: the probe is the authority on whether this worked.
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Whether a path exists WITHOUT following it, so a dangling link counts.
|
|
368
|
+
* @param {string} path Path to inspect.
|
|
369
|
+
* @returns {boolean} True when something occupies the path.
|
|
370
|
+
*/
|
|
371
|
+
function lstatPresent(path) {
|
|
372
|
+
try {
|
|
373
|
+
lstatSync(path);
|
|
374
|
+
return true;
|
|
375
|
+
} catch {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
300
380
|
export function installEntry(entry, row, deps = {}) {
|
|
301
381
|
const run =
|
|
302
382
|
deps.run ??
|
|
@@ -398,6 +478,10 @@ export function installEntry(entry, row, deps = {}) {
|
|
|
398
478
|
};
|
|
399
479
|
}
|
|
400
480
|
|
|
481
|
+
// Link it into ~/.local/bin BEFORE probing, because that is what makes the
|
|
482
|
+
// probe meaningful for a tool the vendor put somewhere else.
|
|
483
|
+
linkIntoBinDir(entry, deps);
|
|
484
|
+
|
|
401
485
|
// Verify by looking, not by exit status. Every installer above can exit 0
|
|
402
486
|
// having installed nothing, so a claim of success is only as good as a fresh
|
|
403
487
|
// probe — reporting a tool as installed when it is absent is worse than
|
|
@@ -12,11 +12,26 @@ Prepare a remote surface so a host project can execute there. Today that means *
|
|
|
12
12
|
|
|
13
13
|
The remote environment's own configuration fields stay **one line into the repository**. Nothing else is pasted into a vendor UI.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
**Ask for that line; do not copy it from here.**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx -y @codyswann/lisa@latest remote-env --emit=claude-web
|
|
18
19
|
```
|
|
19
20
|
|
|
21
|
+
The field pins the exact Lisa it runs, and the emitter reads that version from
|
|
22
|
+
the package it is running out of — so the line it prints is always pinned to the
|
|
23
|
+
version that printed it.
|
|
24
|
+
|
|
25
|
+
A copy written into this file cannot have that property. It is a snapshot taken
|
|
26
|
+
when the package was *built*, and the release bumps the version *after* the
|
|
27
|
+
build, so a published tarball's copy names the previous release. That is not a
|
|
28
|
+
cosmetic lag: the pinned version predates whatever this file documents, so a
|
|
29
|
+
pasted copy can call flags that version does not implement. Measured on
|
|
30
|
+
`2.338.1`, whose embedded copy named `2.338.0` — a Lisa with no `--print-tools`,
|
|
31
|
+
no `--tools`, and no `tools-from-notes.mjs`. Unknown flags are ignored rather
|
|
32
|
+
than rejected, so it did not fail; it installed the full catalogue and re-ran the
|
|
33
|
+
whole setup, three times over, inside a five-minute budget.
|
|
34
|
+
|
|
20
35
|
**That line is identical for every project AND every surface.** Nothing in it names the
|
|
21
36
|
repository or the package manager, so it can be pasted unchanged anywhere.
|
|
22
37
|
|
|
@@ -236,8 +251,10 @@ When emitting, produce exactly:
|
|
|
236
251
|
```text
|
|
237
252
|
Environment name: <project> remote executor
|
|
238
253
|
Repository: <org>/<repo> (must be the default checkout)
|
|
239
|
-
Setup script:
|
|
240
|
-
|
|
254
|
+
Setup script: <printed by `remote-env --emit`; never copied
|
|
255
|
+
from prose, so its pin is always the version
|
|
256
|
+
that printed it>
|
|
257
|
+
Maintenance: <same line, printed by the same command>
|
|
241
258
|
(identical for every project — the script finds the
|
|
242
259
|
checkout and installs from the committed lockfile)
|
|
243
260
|
Environment vars: LISA_SECRETS_SURFACE=codex-cloud
|
|
@@ -912,6 +912,21 @@ export function emitClaudeWeb({ bootstrapKey }) {
|
|
|
912
912
|
" is why exactly one value needs to live here.",
|
|
913
913
|
"",
|
|
914
914
|
" Setup script:",
|
|
915
|
+
"",
|
|
916
|
+
" A setup script sees NONE of the variables configured above — it runs",
|
|
917
|
+
" while the image is being built, before the environment exists. So a",
|
|
918
|
+
" session with no checkout has to be told its tenant inside the script",
|
|
919
|
+
" itself, or the field finds nothing to prepare and exits 0 looking like",
|
|
920
|
+
" success. Put these three lines FIRST, then the line after them:",
|
|
921
|
+
"",
|
|
922
|
+
" export LISA_TENANT=<your namespace>",
|
|
923
|
+
` export ${key}='<the same value as above>'`,
|
|
924
|
+
" export LISA_SECRETS_SURFACE=claude-web",
|
|
925
|
+
"",
|
|
926
|
+
" LISA_SECRETS_SURFACE is set explicitly rather than left to detection:",
|
|
927
|
+
" detection keys off CLAUDE_CODE_REMOTE, and a surface that does not set",
|
|
928
|
+
" it falls through to `local`, which writes no credentials to disk at all.",
|
|
929
|
+
"",
|
|
915
930
|
` ${SETUP_FIELD}`,
|
|
916
931
|
"",
|
|
917
932
|
" This line is identical for every project and every surface — nothing",
|
|
@@ -34,6 +34,8 @@
|
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
import { execFileSync } from "node:child_process";
|
|
37
|
+
import { existsSync, lstatSync, mkdirSync, rmSync, symlinkSync } from "node:fs";
|
|
38
|
+
import { join } from "node:path";
|
|
37
39
|
|
|
38
40
|
import {
|
|
39
41
|
AGENTS,
|
|
@@ -297,6 +299,84 @@ export function renderPlan(plan) {
|
|
|
297
299
|
* @param {object} [deps] Injected runner, for tests.
|
|
298
300
|
* @returns {object} Result row.
|
|
299
301
|
*/
|
|
302
|
+
/**
|
|
303
|
+
* Make a tool that installed elsewhere reachable from `~/.local/bin`.
|
|
304
|
+
*
|
|
305
|
+
* A vendor script installs where it likes — SonarQube's puts `sonar` under
|
|
306
|
+
* `~/.local/share/sonarqube-cli/bin` — and the catalogue records that in
|
|
307
|
+
* `binDir`. Until now the only thing that put those directories on PATH was an
|
|
308
|
+
* edit to the shell rc files, which a cloud container never reads: its tool
|
|
309
|
+
* shell is not a login shell. So the tool installed, and the session that asked
|
|
310
|
+
* for it could not find it. Observed on a live Claude Tag channel, where
|
|
311
|
+
* `~/.local/share/sonarqube-cli` existed and `command -v sonar` came back empty.
|
|
312
|
+
*
|
|
313
|
+
* Exporting PATH from the setup script does not fix that either — those exports
|
|
314
|
+
* die with the process that ran them. Only the filesystem survives into the
|
|
315
|
+
* session, so the fix has to be a file.
|
|
316
|
+
*
|
|
317
|
+
* A symlink into `~/.local/bin` is the shape the AWS installer already uses
|
|
318
|
+
* (`aws -> ~/.local/aws-cli/v2/current/bin/aws`), and that directory is the one
|
|
319
|
+
* place every surface agrees is on PATH.
|
|
320
|
+
*
|
|
321
|
+
* Deliberately never fatal. A tool that is already reachable does not need the
|
|
322
|
+
* link, and one that cannot be linked is caught a moment later by the probe —
|
|
323
|
+
* which reports the real problem instead of a symlink error standing in for it.
|
|
324
|
+
* @param {object} entry Catalogue entry just installed.
|
|
325
|
+
* @param {object} [deps] Injected seams, for tests.
|
|
326
|
+
*/
|
|
327
|
+
export function linkIntoBinDir(entry, deps = {}) {
|
|
328
|
+
if (!entry.binDir) return;
|
|
329
|
+
|
|
330
|
+
const home = deps.home ?? process.env.HOME ?? "";
|
|
331
|
+
const link = deps.link ?? symlinkSync;
|
|
332
|
+
const exists = deps.exists ?? existsSync;
|
|
333
|
+
const remove = deps.remove ?? rmSync;
|
|
334
|
+
const makeDir = deps.mkdir ?? mkdirSync;
|
|
335
|
+
// Two different questions, and they need two different probes.
|
|
336
|
+
//
|
|
337
|
+
// The SOURCE must be followed: a link pointing at a binary that is not there
|
|
338
|
+
// means the install did not produce one, whatever the link says.
|
|
339
|
+
//
|
|
340
|
+
// The TARGET must NOT be followed. `existsSync` reports false for a dangling
|
|
341
|
+
// symlink, which is precisely the case this function claims to repair — the
|
|
342
|
+
// vendor moved and the old link now points at nothing. Following it would
|
|
343
|
+
// skip the removal, `symlinkSync` would throw EEXIST, the catch below would
|
|
344
|
+
// swallow it, and the broken link would survive the fix meant to replace it.
|
|
345
|
+
const present = deps.present ?? (path => lstatPresent(path));
|
|
346
|
+
|
|
347
|
+
const source = join(
|
|
348
|
+
entry.binDir.replace(/^~/, home),
|
|
349
|
+
entry.binary ?? entry.name
|
|
350
|
+
);
|
|
351
|
+
const target = join(home, ".local", "bin", entry.name);
|
|
352
|
+
if (source === target || !exists(source)) return;
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
makeDir(join(home, ".local", "bin"), { recursive: true });
|
|
356
|
+
// Replaced rather than skipped-if-present: a stale link left by an earlier
|
|
357
|
+
// version pointing at a path the vendor has since moved is worse than none,
|
|
358
|
+
// because it resolves and then fails at the moment of use.
|
|
359
|
+
if (present(target)) remove(target, { force: true });
|
|
360
|
+
link(source, target);
|
|
361
|
+
} catch {
|
|
362
|
+
// See above: the probe is the authority on whether this worked.
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Whether a path exists WITHOUT following it, so a dangling link counts.
|
|
368
|
+
* @param {string} path Path to inspect.
|
|
369
|
+
* @returns {boolean} True when something occupies the path.
|
|
370
|
+
*/
|
|
371
|
+
function lstatPresent(path) {
|
|
372
|
+
try {
|
|
373
|
+
lstatSync(path);
|
|
374
|
+
return true;
|
|
375
|
+
} catch {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
300
380
|
export function installEntry(entry, row, deps = {}) {
|
|
301
381
|
const run =
|
|
302
382
|
deps.run ??
|
|
@@ -398,6 +478,10 @@ export function installEntry(entry, row, deps = {}) {
|
|
|
398
478
|
};
|
|
399
479
|
}
|
|
400
480
|
|
|
481
|
+
// Link it into ~/.local/bin BEFORE probing, because that is what makes the
|
|
482
|
+
// probe meaningful for a tool the vendor put somewhere else.
|
|
483
|
+
linkIntoBinDir(entry, deps);
|
|
484
|
+
|
|
401
485
|
// Verify by looking, not by exit status. Every installer above can exit 0
|
|
402
486
|
// having installed nothing, so a claim of success is only as good as a fresh
|
|
403
487
|
// probe — reporting a tool as installed when it is absent is worse than
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.338.
|
|
3
|
+
"version": "2.338.3",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.338.
|
|
3
|
+
"version": "2.338.3",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.338.
|
|
3
|
+
"version": "2.338.3",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.338.
|
|
3
|
+
"version": "2.338.3",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.338.
|
|
3
|
+
"version": "2.338.3",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|