@songsid/agend 2.2.0-alpha.1 → 2.2.0-alpha.2
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/README.md +2 -0
- package/README.zh-TW.md +2 -0
- package/dist/agent-cli.js +6 -1
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +68 -12
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-token-header.d.ts +13 -0
- package/dist/agent-token-header.js +16 -0
- package/dist/agent-token-header.js.map +1 -0
- package/dist/backend/antigravity.js +4 -3
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/grok.js +1 -1
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro.d.ts +7 -0
- package/dist/backend/kiro.js +82 -33
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/muse.js +1 -1
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/types.d.ts +6 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/cache-analysis.d.ts +92 -0
- package/dist/cache-analysis.js +147 -0
- package/dist/cache-analysis.js.map +1 -0
- package/dist/cache-ledger.d.ts +108 -0
- package/dist/cache-ledger.js +230 -0
- package/dist/cache-ledger.js.map +1 -0
- package/dist/cache-prices.d.ts +32 -0
- package/dist/cache-prices.js +73 -0
- package/dist/cache-prices.js.map +1 -0
- package/dist/cache-scan.d.ts +39 -0
- package/dist/cache-scan.js +115 -0
- package/dist/cache-scan.js.map +1 -0
- package/dist/cache-service.d.ts +115 -0
- package/dist/cache-service.js +417 -0
- package/dist/cache-service.js.map +1 -0
- package/dist/cli-entry.d.ts +19 -0
- package/dist/cli-entry.js +36 -0
- package/dist/cli-entry.js.map +1 -0
- package/dist/cli.js +251 -24
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +6 -0
- package/dist/daemon.js +90 -37
- package/dist/daemon.js.map +1 -1
- package/dist/event-log.d.ts +4 -1
- package/dist/event-log.js +6 -3
- package/dist/event-log.js.map +1 -1
- package/dist/fleet-control-audit.d.ts +5 -0
- package/dist/fleet-control-audit.js +7 -0
- package/dist/fleet-control-audit.js.map +1 -1
- package/dist/fleet-level-config.js +22 -0
- package/dist/fleet-level-config.js.map +1 -1
- package/dist/fleet-manager.d.ts +46 -0
- package/dist/fleet-manager.js +287 -40
- package/dist/fleet-manager.js.map +1 -1
- package/dist/full-restart.js +2 -3
- package/dist/full-restart.js.map +1 -1
- package/dist/general-knowledge/skills/persona-emoji/SKILL.md +3 -1
- package/dist/install-lock.d.ts +49 -0
- package/dist/install-lock.js +200 -0
- package/dist/install-lock.js.map +1 -0
- package/dist/instance-config-impact.js +3 -2
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/locale.js +14 -0
- package/dist/locale.js.map +1 -1
- package/dist/outbound-schemas.js +1 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/package-preimage.d.ts +26 -0
- package/dist/package-preimage.js +96 -0
- package/dist/package-preimage.js.map +1 -0
- package/dist/public-web-gateway.js +2 -2
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/quickstart.js +10 -4
- package/dist/quickstart.js.map +1 -1
- package/dist/restart-guard.d.ts +52 -0
- package/dist/restart-guard.js +109 -0
- package/dist/restart-guard.js.map +1 -0
- package/dist/scheduler/db.js +6 -2
- package/dist/scheduler/db.js.map +1 -1
- package/dist/service-activation.d.ts +47 -5
- package/dist/service-activation.js +156 -51
- package/dist/service-activation.js.map +1 -1
- package/dist/service-installer.d.ts +56 -1
- package/dist/service-installer.js +134 -12
- package/dist/service-installer.js.map +1 -1
- package/dist/service-plan.d.ts +65 -0
- package/dist/service-plan.js +108 -0
- package/dist/service-plan.js.map +1 -0
- package/dist/settings-api.js +46 -5
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +34 -6
- package/dist/settings-change.js.map +1 -1
- package/dist/setup-host.js +2 -3
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-wizard.js +2 -1
- package/dist/setup-wizard.js.map +1 -1
- package/dist/sqlite-open-errors.d.ts +12 -0
- package/dist/sqlite-open-errors.js +13 -0
- package/dist/sqlite-open-errors.js.map +1 -0
- package/dist/tmux-control.d.ts +22 -0
- package/dist/tmux-control.js +89 -5
- package/dist/tmux-control.js.map +1 -1
- package/dist/topic-commands.js +14 -5
- package/dist/topic-commands.js.map +1 -1
- package/dist/ui/chat-commands.js +43 -0
- package/dist/ui/chat-render.js +2 -2
- package/dist/ui/chat-store.js +30 -1
- package/dist/ui/chat-strings.js +22 -2
- package/dist/ui/chat-thread.js +47 -10
- package/dist/ui/fleet-cache.js +168 -0
- package/dist/ui/panel-chat.js +104 -18
- package/dist/ui/panel-fleet.js +26 -12
- package/dist/ui/panel-settings.js +12 -8
- package/dist/ui/preview-panel.js +202 -0
- package/dist/ui/preview.js +7 -3
- package/dist/ui/settings-strings.js +4 -2
- package/dist/ui/shared/app-route.js +1 -1
- package/dist/ui/shared/app-shell.js +6 -4
- package/dist/ui/shared/app.css +147 -3
- package/dist/ui/shared/signin.js +13 -1
- package/dist/ui/shared/ui-icons.js +2 -0
- package/dist/ui/signin.html +2 -1
- package/dist/update-dispatch.d.ts +28 -0
- package/dist/update-dispatch.js +73 -0
- package/dist/update-dispatch.js.map +1 -0
- package/dist/update-install.d.ts +65 -7
- package/dist/update-install.js +126 -26
- package/dist/update-install.js.map +1 -1
- package/dist/web-api.d.ts +14 -0
- package/dist/web-api.js +65 -0
- package/dist/web-api.js.map +1 -1
- package/dist/web-commands.d.ts +77 -0
- package/dist/web-commands.js +81 -0
- package/dist/web-commands.js.map +1 -0
- package/dist/web-shell-routes.d.ts +1 -1
- package/dist/web-shell-routes.js +1 -1
- package/dist/web-shell-routes.js.map +1 -1
- package/launcher/agend +110 -0
- package/launcher/agend-agent +110 -0
- package/launcher/agend-agent.cjs +4 -0
- package/launcher/agend.cjs +4 -0
- package/launcher/install-admission.cjs +105 -0
- package/launcher/launch.cjs +64 -0
- package/launcher/postinstall.cjs +114 -0
- package/launcher/run-in-process.cjs +13 -0
- package/launcher/runtime-platform.cjs +86 -0
- package/launcher/runtime-select.cjs +217 -0
- package/package.json +12 -4
- package/scripts/preinstall-guard.cjs +40 -21
- package/templates/launchd.plist.ejs +2 -2
- package/templates/systemd.service.ejs +1 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1269 (4): an instance's chat commands, run from the web chat. Only what the instance's own Discord/Telegram topic
|
|
3
|
+
* already accepts, through the same handlers (topic-commands.ts / FleetManager) and the same table (command-table.ts,
|
|
4
|
+
* #754/#1148): nothing here is a capability the platforms do not have.
|
|
5
|
+
*
|
|
6
|
+
* - Who: a signed-in web session holds the full-fleet credential, issued only to a fleet admin (/dashboard is
|
|
7
|
+
* fleet-admin only), so the table's admin levels are met — exactly as a web click on a mirrored prompt is answered
|
|
8
|
+
* "as a fleet admin on the platform". The table can still refuse a command in a kind of channel; that stands.
|
|
9
|
+
* - What: instance commands only. Fleet-level ones (/status, /restart, /update, /login, …) stay in General
|
|
10
|
+
* (Discord-first), and /raw is not a command here (it bypasses the [user:] envelope).
|
|
11
|
+
* - The public link (a gateway request) is the outward-facing surface: /save is refused there — it makes the CLI
|
|
12
|
+
* write a file under a name the caller picks, outside the agent's own judgement.
|
|
13
|
+
* - /clear asks first, as on the platforms: the first call answers a confirmation token; only the second, with that
|
|
14
|
+
* token, clears — and only if the instance is still the one that was asked about.
|
|
15
|
+
*/
|
|
16
|
+
import { type CommandScope } from "./command-table.js";
|
|
17
|
+
export declare const WEB_COMMANDS: readonly ["ctx", "compact", "clear", "model", "effort", "cancel", "btw", "steer", "pause", "wake", "save"];
|
|
18
|
+
export type WebCommand = typeof WEB_COMMANDS[number];
|
|
19
|
+
/** Refused on the public link (see the header). */
|
|
20
|
+
export declare const PUBLIC_LINK_REFUSED: ReadonlySet<WebCommand>;
|
|
21
|
+
export declare const MAX_COMMAND_ARGS = 4000;
|
|
22
|
+
export interface WebChoices {
|
|
23
|
+
current: string | null;
|
|
24
|
+
options: Array<{
|
|
25
|
+
id: string;
|
|
26
|
+
label: string;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
export interface WebCommandDeps {
|
|
30
|
+
/** The instance is one the web chat may command (a fleet instance or a ClassicBot room), and in which scope. */
|
|
31
|
+
scope(instance: string): CommandScope | null;
|
|
32
|
+
ctx(instance: string): Promise<string>;
|
|
33
|
+
compact(instance: string, instructions: string | undefined): Promise<string>;
|
|
34
|
+
applyModel(instance: string, name: string): Promise<string>;
|
|
35
|
+
/** The model menu from the cached catalog only (never a live probe on this path); null: no list to offer. */
|
|
36
|
+
modelChoices(instance: string): WebChoices | null;
|
|
37
|
+
applyEffort(instance: string, level: string): Promise<string>;
|
|
38
|
+
effortChoices(instance: string): WebChoices | null;
|
|
39
|
+
cancel(instance: string): boolean;
|
|
40
|
+
steer(instance: string, text: string): string;
|
|
41
|
+
btw(instance: string, text: string): string;
|
|
42
|
+
pauseWake(instance: string, action: "pause" | "wake"): Promise<string>;
|
|
43
|
+
save(instance: string, filename: string): Promise<string>;
|
|
44
|
+
/** Ask: a confirmation token (or why not). Confirm: clear, if the token is live and the instance unchanged. */
|
|
45
|
+
clearAsk(instance: string): {
|
|
46
|
+
token: string;
|
|
47
|
+
message: string;
|
|
48
|
+
} | {
|
|
49
|
+
refused: string;
|
|
50
|
+
};
|
|
51
|
+
clearConfirm(instance: string, token: string): Promise<{
|
|
52
|
+
status: number;
|
|
53
|
+
text: string;
|
|
54
|
+
}>;
|
|
55
|
+
t(key: string, ...args: string[]): string;
|
|
56
|
+
}
|
|
57
|
+
export interface WebCommandInput {
|
|
58
|
+
instance: string;
|
|
59
|
+
command: string;
|
|
60
|
+
args?: string;
|
|
61
|
+
confirm?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface WebCommandResult {
|
|
64
|
+
status: number;
|
|
65
|
+
body: {
|
|
66
|
+
text?: string;
|
|
67
|
+
error?: string;
|
|
68
|
+
choices?: WebChoices;
|
|
69
|
+
confirm?: {
|
|
70
|
+
token: string;
|
|
71
|
+
message: string;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export declare function runWebCommand(deps: WebCommandDeps, input: WebCommandInput, opts: {
|
|
76
|
+
publicLink: boolean;
|
|
77
|
+
}): Promise<WebCommandResult>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1269 (4): an instance's chat commands, run from the web chat. Only what the instance's own Discord/Telegram topic
|
|
3
|
+
* already accepts, through the same handlers (topic-commands.ts / FleetManager) and the same table (command-table.ts,
|
|
4
|
+
* #754/#1148): nothing here is a capability the platforms do not have.
|
|
5
|
+
*
|
|
6
|
+
* - Who: a signed-in web session holds the full-fleet credential, issued only to a fleet admin (/dashboard is
|
|
7
|
+
* fleet-admin only), so the table's admin levels are met — exactly as a web click on a mirrored prompt is answered
|
|
8
|
+
* "as a fleet admin on the platform". The table can still refuse a command in a kind of channel; that stands.
|
|
9
|
+
* - What: instance commands only. Fleet-level ones (/status, /restart, /update, /login, …) stay in General
|
|
10
|
+
* (Discord-first), and /raw is not a command here (it bypasses the [user:] envelope).
|
|
11
|
+
* - The public link (a gateway request) is the outward-facing surface: /save is refused there — it makes the CLI
|
|
12
|
+
* write a file under a name the caller picks, outside the agent's own judgement.
|
|
13
|
+
* - /clear asks first, as on the platforms: the first call answers a confirmation token; only the second, with that
|
|
14
|
+
* token, clears — and only if the instance is still the one that was asked about.
|
|
15
|
+
*/
|
|
16
|
+
import { commandSpec, decideCommand } from "./command-table.js";
|
|
17
|
+
import { SAVE_FILENAME_RE } from "./topic-commands.js";
|
|
18
|
+
export const WEB_COMMANDS = ["ctx", "compact", "clear", "model", "effort", "cancel", "btw", "steer", "pause", "wake", "save"];
|
|
19
|
+
/** Refused on the public link (see the header). */
|
|
20
|
+
export const PUBLIC_LINK_REFUSED = new Set(["save"]);
|
|
21
|
+
export const MAX_COMMAND_ARGS = 4000;
|
|
22
|
+
const isWebCommand = (c) => WEB_COMMANDS.includes(c);
|
|
23
|
+
export async function runWebCommand(deps, input, opts) {
|
|
24
|
+
const { instance } = input;
|
|
25
|
+
const command = input.command.replace(/^\//, "");
|
|
26
|
+
if (!isWebCommand(command))
|
|
27
|
+
return { status: 400, body: { error: deps.t("web.command_unknown", `/${command}`) } };
|
|
28
|
+
if (opts.publicLink && PUBLIC_LINK_REFUSED.has(command))
|
|
29
|
+
return { status: 403, body: { error: deps.t("web.command_public_refused", `/${command}`) } };
|
|
30
|
+
const scope = deps.scope(instance);
|
|
31
|
+
if (!scope)
|
|
32
|
+
return { status: 404, body: { error: deps.t("instance.not_found", instance) } };
|
|
33
|
+
// The same table the platforms use; the session is fleet-admin level, so only a scope refusal can stand.
|
|
34
|
+
const spec = commandSpec(command);
|
|
35
|
+
if (!spec)
|
|
36
|
+
return { status: 400, body: { error: deps.t("web.command_unknown", `/${command}`) } };
|
|
37
|
+
const decision = decideCommand(spec, scope, { fleetAdmin: () => "ok", channelAdmin: () => true, classicAdmin: () => true });
|
|
38
|
+
if (!decision.allow)
|
|
39
|
+
return { status: 403, body: { error: deps.t(...decision.reply) } };
|
|
40
|
+
const args = (input.args ?? "").trim();
|
|
41
|
+
const ok = (text) => ({ status: 200, body: { text } });
|
|
42
|
+
switch (command) {
|
|
43
|
+
case "ctx": return ok(await deps.ctx(instance));
|
|
44
|
+
case "compact": return ok(await deps.compact(instance, args || undefined));
|
|
45
|
+
case "model": {
|
|
46
|
+
if (args)
|
|
47
|
+
return ok(await deps.applyModel(instance, args));
|
|
48
|
+
const choices = deps.modelChoices(instance);
|
|
49
|
+
return choices ? { status: 200, body: { choices } } : ok(deps.t("model.list_unavailable", instance));
|
|
50
|
+
}
|
|
51
|
+
case "effort": {
|
|
52
|
+
if (args)
|
|
53
|
+
return ok(await deps.applyEffort(instance, args));
|
|
54
|
+
const choices = deps.effortChoices(instance);
|
|
55
|
+
return choices ? { status: 200, body: { choices } } : { status: 400, body: { error: deps.t("web.command_no_choices", "/effort") } };
|
|
56
|
+
}
|
|
57
|
+
case "cancel": return ok(deps.cancel(instance) ? deps.t("cancel.sent", instance) : deps.t("cancel.not_running", instance));
|
|
58
|
+
case "steer":
|
|
59
|
+
case "btw":
|
|
60
|
+
if (!args)
|
|
61
|
+
return { status: 400, body: { error: deps.t(`${command}.usage`) } };
|
|
62
|
+
return ok(command === "steer" ? deps.steer(instance, args) : deps.btw(instance, args));
|
|
63
|
+
case "pause":
|
|
64
|
+
case "wake": return ok(await deps.pauseWake(instance, command));
|
|
65
|
+
case "save":
|
|
66
|
+
if (!args)
|
|
67
|
+
return { status: 400, body: { error: deps.t("save.usage") } };
|
|
68
|
+
if (!SAVE_FILENAME_RE.test(args))
|
|
69
|
+
return { status: 400, body: { error: deps.t("filename.invalid") } };
|
|
70
|
+
return ok(await deps.save(instance, args));
|
|
71
|
+
case "clear": {
|
|
72
|
+
if (input.confirm) {
|
|
73
|
+
const r = await deps.clearConfirm(instance, input.confirm);
|
|
74
|
+
return { status: r.status, body: r.status === 200 ? { text: r.text } : { error: r.text } };
|
|
75
|
+
}
|
|
76
|
+
const asked = deps.clearAsk(instance);
|
|
77
|
+
return "token" in asked ? { status: 200, body: { confirm: asked } } : { status: 409, body: { error: asked.refused } };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=web-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-commands.js","sourceRoot":"","sources":["../src/web-commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* view-api.ts serves them, open to anyone when `web.view_access` is open). /settings and /settings/<section> are the
|
|
11
11
|
* Settings panel (step 3; settings-api.ts serves them, signed in only).
|
|
12
12
|
*/
|
|
13
|
-
export declare const FLEET_TABS: readonly ["tasks", "schedules", "teams", "org", "config"];
|
|
13
|
+
export declare const FLEET_TABS: readonly ["tasks", "schedules", "teams", "org", "cache", "config"];
|
|
14
14
|
export type FleetTab = typeof FLEET_TABS[number];
|
|
15
15
|
export declare const SETTINGS_SECTIONS: readonly ["agents", "bots", "classic", "general", "advanced"];
|
|
16
16
|
export type SettingsSection = typeof SETTINGS_SECTIONS[number];
|
package/dist/web-shell-routes.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* view-api.ts serves them, open to anyone when `web.view_access` is open). /settings and /settings/<section> are the
|
|
11
11
|
* Settings panel (step 3; settings-api.ts serves them, signed in only).
|
|
12
12
|
*/
|
|
13
|
-
export const FLEET_TABS = ["tasks", "schedules", "teams", "org", "config"];
|
|
13
|
+
export const FLEET_TABS = ["tasks", "schedules", "teams", "org", "cache", "config"];
|
|
14
14
|
export const SETTINGS_SECTIONS = ["agents", "bots", "classic", "general", "advanced"];
|
|
15
15
|
const PATH_SEPARATOR_OR_CONTROL = /[/\\\u0000-\u001f\u007f]/;
|
|
16
16
|
/** Longest instance name a path may carry; real names are far shorter. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-shell-routes.js","sourceRoot":"","sources":["../src/web-shell-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"web-shell-routes.js","sourceRoot":"","sources":["../src/web-shell-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAU,CAAC;AAE7F,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,CAAU,CAAC;AAa/F,MAAM,yBAAyB,GAAG,0BAA0B,CAAC;AAC7D,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAErC;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,iBAAiB,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC;WAC9F,IAAI,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED,2EAA2E;AAC3E,SAAS,aAAa,CAAC,OAAe;IACpC,IAAI,CAAC;QAAC,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AACpE,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,IAAY;IACrD,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;IACvF,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC;IAC5F,8GAA8G;IAC9G,wCAAwC;IACxC,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC;IAC9E,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,KAAK;QAAE,OAAQ,UAAgC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAa,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACzJ,MAAM,IAAI,GAAG,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;QACrC,OAAO,IAAI,KAAK,IAAI,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IACzI,CAAC;IACD,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;IACzF,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;QACrC,OAAO,IAAI,KAAK,IAAI,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IACzI,CAAC;IACD,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC;IACpG,MAAM,QAAQ,GAAG,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAQ,iBAAuC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC;YACpE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAoB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACtG,CAAC;IACD,sGAAsG;IACtG,OAAO,IAAI,CAAC;AACd,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAClC,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU,CAAC;AAC1E,CAAC;AAED,0GAA0G;AAC1G,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAClC,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC;AACvG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC;AAC1C,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,QAAQ,CAAC,QAAgB;IACvC,OAAO,YAAY,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;AACpD,CAAC;AACD,MAAM,UAAU,QAAQ,CAAC,QAAgB;IACvC,OAAO,SAAS,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;AACjD,CAAC"}
|
package/launcher/agend
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# #1450: the `agend` and `agend-agent` npm bins (this file and launcher/agend-agent are identical). POSIX sh, so AgEnD
|
|
3
|
+
# starts with no Node on PATH at all: it execs this release's bundled Node on the JS launcher, which is the one place
|
|
4
|
+
# that decides and verifies the interpreter (launcher/runtime-select.cjs). A `node` on PATH is used only when the
|
|
5
|
+
# bundled Node is not installed; the JS launcher then decides whether that Node qualifies. Native Windows is not
|
|
6
|
+
# supported (WSL is), as with tmux.
|
|
7
|
+
|
|
8
|
+
# This file's real location: npm's global bin is a symlink, possibly relative, possibly to another link. A readlink
|
|
9
|
+
# loop, not `readlink -f` (older macOS lacks it; GNU is not assumed).
|
|
10
|
+
self=$0
|
|
11
|
+
while [ -L "$self" ]; do
|
|
12
|
+
link=$(readlink "$self") || break
|
|
13
|
+
case $link in
|
|
14
|
+
/*) self=$link ;;
|
|
15
|
+
*) self=$(dirname "$self")/$link ;;
|
|
16
|
+
esac
|
|
17
|
+
done
|
|
18
|
+
dir=$(cd "$(dirname "$self")" && pwd -P) || exit 1
|
|
19
|
+
pkg=$(dirname "$dir")
|
|
20
|
+
launcher=$dir/$(basename "$self").cjs
|
|
21
|
+
|
|
22
|
+
sys=$(uname -s) || sys=unknown
|
|
23
|
+
mach=$(uname -m) || mach=unknown
|
|
24
|
+
case $sys in
|
|
25
|
+
Linux) os=linux ;;
|
|
26
|
+
Darwin) os=darwin ;;
|
|
27
|
+
*) os=unsupported ;;
|
|
28
|
+
esac
|
|
29
|
+
case $mach in
|
|
30
|
+
x86_64 | amd64) cpu=x64 ;;
|
|
31
|
+
aarch64 | arm64) cpu=arm64 ;;
|
|
32
|
+
*) cpu=unsupported ;;
|
|
33
|
+
esac
|
|
34
|
+
node_pkg=$pkg/node_modules/@songsid/agend-node-$os-$cpu
|
|
35
|
+
node=$node_pkg/bin/node
|
|
36
|
+
receipt=$pkg/.agend-runtime.json
|
|
37
|
+
repair="npm install -g @songsid/agend"
|
|
38
|
+
refuse() { echo "AgEnD cannot start: $1." >&2; echo "To repair: $2" >&2; exit "${3:-1}"; }
|
|
39
|
+
|
|
40
|
+
# 1. AGEND_NODE, the operator's explicit choice: absolute and executable, then it runs the JS launcher, whose probe
|
|
41
|
+
# refuses one that does not qualify. Never a silent fallback to another Node.
|
|
42
|
+
if [ -n "${AGEND_NODE:-}" ]; then
|
|
43
|
+
case $AGEND_NODE in
|
|
44
|
+
/*) ;;
|
|
45
|
+
*) refuse "AGEND_NODE must be an absolute path (got $AGEND_NODE)" "unset AGEND_NODE, or point it at a Node 22.14+" ;;
|
|
46
|
+
esac
|
|
47
|
+
[ -f "$AGEND_NODE" ] && [ -x "$AGEND_NODE" ] || refuse "AGEND_NODE ($AGEND_NODE) is not an executable file" "unset AGEND_NODE, or point it at a Node 22.14+"
|
|
48
|
+
exec "$AGEND_NODE" "$launcher" "$@"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# 2. The bundled Node, only when its ADMISSION KEY (launcher/runtime-select.cjs, "THE ADMISSION KEY") is exactly what
|
|
52
|
+
# this shell measures now — nothing parsed, the bytes rebuilt and compared: the host's os and cpu (never its
|
|
53
|
+
# version: an OS update must not lock AgEnD out), the Node's realpath, size and mtime, and the POSIX cksum of both
|
|
54
|
+
# manifests and of the receipt. The JS selection requires the same key, so a
|
|
55
|
+
# change to any bound file keeps BOTH from running the candidate.
|
|
56
|
+
# Every measurement must itself complete: a value printed by a command that then fails is no measurement (#1460 r4).
|
|
57
|
+
admissible() {
|
|
58
|
+
[ -f "$pkg/.agend-runtime.key" ] && [ -f "$node" ] && [ ! -L "$node" ] && [ -x "$node" ] || return 1
|
|
59
|
+
case $os in linux | darwin) ;; *) return 1 ;; esac
|
|
60
|
+
real=$(cd "${node%/*}" 2>/dev/null && pwd -P) || return 1
|
|
61
|
+
real=$real/node
|
|
62
|
+
size=$(wc -c < "$node") || return 1
|
|
63
|
+
set -- $size
|
|
64
|
+
size=${1:-}
|
|
65
|
+
case $size in '' | *[!0-9]*) return 1 ;; esac
|
|
66
|
+
if mtime=$(stat -c %Y "$node" 2>/dev/null); then :; elif mtime=$(stat -f %m "$node" 2>/dev/null); then :; else return 1; fi
|
|
67
|
+
case $mtime in '' | *[!0-9]*) return 1 ;; esac
|
|
68
|
+
sum_package=$(cksum < "$pkg/package.json") || return 1
|
|
69
|
+
sum_runtime=$(cksum < "$node_pkg/package.json") || return 1
|
|
70
|
+
sum_receipt=$(cksum < "$receipt") || return 1
|
|
71
|
+
printf 'agend-runtime-key 2\nos %s\ncpu %s\nnode %s\nsize %s\nmtime %s\npackage %s\nruntime %s\nreceipt %s\n' \
|
|
72
|
+
"$os" "$cpu" "$real" "$size" "$mtime" "$sum_package" "$sum_runtime" "$sum_receipt" | cmp -s - "$pkg/.agend-runtime.key"
|
|
73
|
+
}
|
|
74
|
+
# This host, measured fresh, must still be one the bundled Node supports (launcher/runtime-platform.cjs
|
|
75
|
+
# runtimeSupport: glibc >= 2.28 on Linux, Darwin kernel >= 20) — minimums, never a pinned version. A stored or copied
|
|
76
|
+
# runtime on a host that no longer qualifies never runs; the JS launcher then picks a system Node or refuses.
|
|
77
|
+
supported() {
|
|
78
|
+
case $os in
|
|
79
|
+
linux)
|
|
80
|
+
v=$(getconf GNU_LIBC_VERSION 2>/dev/null) || return 1
|
|
81
|
+
case $v in "glibc "*) v=${v#glibc } ;; *) return 1 ;; esac
|
|
82
|
+
major=${v%%.*}
|
|
83
|
+
minor=${v#*.}
|
|
84
|
+
minor=${minor%%.*}
|
|
85
|
+
case $major in '' | *[!0-9]*) return 1 ;; esac
|
|
86
|
+
case $minor in '' | *[!0-9]*) return 1 ;; esac
|
|
87
|
+
[ "$major" -gt 2 ] || { [ "$major" -eq 2 ] && [ "$minor" -ge 28 ]; }
|
|
88
|
+
;;
|
|
89
|
+
darwin)
|
|
90
|
+
r=$(uname -r) || return 1
|
|
91
|
+
kernel=${r%%.*}
|
|
92
|
+
case $kernel in '' | *[!0-9]*) return 1 ;; esac
|
|
93
|
+
[ "$kernel" -ge 20 ]
|
|
94
|
+
;;
|
|
95
|
+
*) return 1 ;;
|
|
96
|
+
esac
|
|
97
|
+
}
|
|
98
|
+
if supported && admissible; then
|
|
99
|
+
exec "$node" "$launcher" "$@"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# 3. Not admitted here, so that candidate never runs. A node on PATH runs the JS launcher: it explains a refusal, or
|
|
103
|
+
# is itself the choice when the runtime was skipped altogether (and it qualifies).
|
|
104
|
+
if command -v node >/dev/null 2>&1; then
|
|
105
|
+
exec node "$launcher" "$@"
|
|
106
|
+
fi
|
|
107
|
+
if [ -e "$receipt" ] || [ -e "$node" ] || [ -L "$node" ]; then
|
|
108
|
+
refuse "its bundled Node ($node) is missing, incomplete or changed since it was verified, and there is no node on PATH" "$repair"
|
|
109
|
+
fi
|
|
110
|
+
refuse "its bundled Node is not installed and there is no node on PATH" "$repair" 127
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# #1450: the `agend` and `agend-agent` npm bins (this file and launcher/agend-agent are identical). POSIX sh, so AgEnD
|
|
3
|
+
# starts with no Node on PATH at all: it execs this release's bundled Node on the JS launcher, which is the one place
|
|
4
|
+
# that decides and verifies the interpreter (launcher/runtime-select.cjs). A `node` on PATH is used only when the
|
|
5
|
+
# bundled Node is not installed; the JS launcher then decides whether that Node qualifies. Native Windows is not
|
|
6
|
+
# supported (WSL is), as with tmux.
|
|
7
|
+
|
|
8
|
+
# This file's real location: npm's global bin is a symlink, possibly relative, possibly to another link. A readlink
|
|
9
|
+
# loop, not `readlink -f` (older macOS lacks it; GNU is not assumed).
|
|
10
|
+
self=$0
|
|
11
|
+
while [ -L "$self" ]; do
|
|
12
|
+
link=$(readlink "$self") || break
|
|
13
|
+
case $link in
|
|
14
|
+
/*) self=$link ;;
|
|
15
|
+
*) self=$(dirname "$self")/$link ;;
|
|
16
|
+
esac
|
|
17
|
+
done
|
|
18
|
+
dir=$(cd "$(dirname "$self")" && pwd -P) || exit 1
|
|
19
|
+
pkg=$(dirname "$dir")
|
|
20
|
+
launcher=$dir/$(basename "$self").cjs
|
|
21
|
+
|
|
22
|
+
sys=$(uname -s) || sys=unknown
|
|
23
|
+
mach=$(uname -m) || mach=unknown
|
|
24
|
+
case $sys in
|
|
25
|
+
Linux) os=linux ;;
|
|
26
|
+
Darwin) os=darwin ;;
|
|
27
|
+
*) os=unsupported ;;
|
|
28
|
+
esac
|
|
29
|
+
case $mach in
|
|
30
|
+
x86_64 | amd64) cpu=x64 ;;
|
|
31
|
+
aarch64 | arm64) cpu=arm64 ;;
|
|
32
|
+
*) cpu=unsupported ;;
|
|
33
|
+
esac
|
|
34
|
+
node_pkg=$pkg/node_modules/@songsid/agend-node-$os-$cpu
|
|
35
|
+
node=$node_pkg/bin/node
|
|
36
|
+
receipt=$pkg/.agend-runtime.json
|
|
37
|
+
repair="npm install -g @songsid/agend"
|
|
38
|
+
refuse() { echo "AgEnD cannot start: $1." >&2; echo "To repair: $2" >&2; exit "${3:-1}"; }
|
|
39
|
+
|
|
40
|
+
# 1. AGEND_NODE, the operator's explicit choice: absolute and executable, then it runs the JS launcher, whose probe
|
|
41
|
+
# refuses one that does not qualify. Never a silent fallback to another Node.
|
|
42
|
+
if [ -n "${AGEND_NODE:-}" ]; then
|
|
43
|
+
case $AGEND_NODE in
|
|
44
|
+
/*) ;;
|
|
45
|
+
*) refuse "AGEND_NODE must be an absolute path (got $AGEND_NODE)" "unset AGEND_NODE, or point it at a Node 22.14+" ;;
|
|
46
|
+
esac
|
|
47
|
+
[ -f "$AGEND_NODE" ] && [ -x "$AGEND_NODE" ] || refuse "AGEND_NODE ($AGEND_NODE) is not an executable file" "unset AGEND_NODE, or point it at a Node 22.14+"
|
|
48
|
+
exec "$AGEND_NODE" "$launcher" "$@"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# 2. The bundled Node, only when its ADMISSION KEY (launcher/runtime-select.cjs, "THE ADMISSION KEY") is exactly what
|
|
52
|
+
# this shell measures now — nothing parsed, the bytes rebuilt and compared: the host's os and cpu (never its
|
|
53
|
+
# version: an OS update must not lock AgEnD out), the Node's realpath, size and mtime, and the POSIX cksum of both
|
|
54
|
+
# manifests and of the receipt. The JS selection requires the same key, so a
|
|
55
|
+
# change to any bound file keeps BOTH from running the candidate.
|
|
56
|
+
# Every measurement must itself complete: a value printed by a command that then fails is no measurement (#1460 r4).
|
|
57
|
+
admissible() {
|
|
58
|
+
[ -f "$pkg/.agend-runtime.key" ] && [ -f "$node" ] && [ ! -L "$node" ] && [ -x "$node" ] || return 1
|
|
59
|
+
case $os in linux | darwin) ;; *) return 1 ;; esac
|
|
60
|
+
real=$(cd "${node%/*}" 2>/dev/null && pwd -P) || return 1
|
|
61
|
+
real=$real/node
|
|
62
|
+
size=$(wc -c < "$node") || return 1
|
|
63
|
+
set -- $size
|
|
64
|
+
size=${1:-}
|
|
65
|
+
case $size in '' | *[!0-9]*) return 1 ;; esac
|
|
66
|
+
if mtime=$(stat -c %Y "$node" 2>/dev/null); then :; elif mtime=$(stat -f %m "$node" 2>/dev/null); then :; else return 1; fi
|
|
67
|
+
case $mtime in '' | *[!0-9]*) return 1 ;; esac
|
|
68
|
+
sum_package=$(cksum < "$pkg/package.json") || return 1
|
|
69
|
+
sum_runtime=$(cksum < "$node_pkg/package.json") || return 1
|
|
70
|
+
sum_receipt=$(cksum < "$receipt") || return 1
|
|
71
|
+
printf 'agend-runtime-key 2\nos %s\ncpu %s\nnode %s\nsize %s\nmtime %s\npackage %s\nruntime %s\nreceipt %s\n' \
|
|
72
|
+
"$os" "$cpu" "$real" "$size" "$mtime" "$sum_package" "$sum_runtime" "$sum_receipt" | cmp -s - "$pkg/.agend-runtime.key"
|
|
73
|
+
}
|
|
74
|
+
# This host, measured fresh, must still be one the bundled Node supports (launcher/runtime-platform.cjs
|
|
75
|
+
# runtimeSupport: glibc >= 2.28 on Linux, Darwin kernel >= 20) — minimums, never a pinned version. A stored or copied
|
|
76
|
+
# runtime on a host that no longer qualifies never runs; the JS launcher then picks a system Node or refuses.
|
|
77
|
+
supported() {
|
|
78
|
+
case $os in
|
|
79
|
+
linux)
|
|
80
|
+
v=$(getconf GNU_LIBC_VERSION 2>/dev/null) || return 1
|
|
81
|
+
case $v in "glibc "*) v=${v#glibc } ;; *) return 1 ;; esac
|
|
82
|
+
major=${v%%.*}
|
|
83
|
+
minor=${v#*.}
|
|
84
|
+
minor=${minor%%.*}
|
|
85
|
+
case $major in '' | *[!0-9]*) return 1 ;; esac
|
|
86
|
+
case $minor in '' | *[!0-9]*) return 1 ;; esac
|
|
87
|
+
[ "$major" -gt 2 ] || { [ "$major" -eq 2 ] && [ "$minor" -ge 28 ]; }
|
|
88
|
+
;;
|
|
89
|
+
darwin)
|
|
90
|
+
r=$(uname -r) || return 1
|
|
91
|
+
kernel=${r%%.*}
|
|
92
|
+
case $kernel in '' | *[!0-9]*) return 1 ;; esac
|
|
93
|
+
[ "$kernel" -ge 20 ]
|
|
94
|
+
;;
|
|
95
|
+
*) return 1 ;;
|
|
96
|
+
esac
|
|
97
|
+
}
|
|
98
|
+
if supported && admissible; then
|
|
99
|
+
exec "$node" "$launcher" "$@"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# 3. Not admitted here, so that candidate never runs. A node on PATH runs the JS launcher: it explains a refusal, or
|
|
103
|
+
# is itself the choice when the runtime was skipped altogether (and it qualifies).
|
|
104
|
+
if command -v node >/dev/null 2>&1; then
|
|
105
|
+
exec node "$launcher" "$@"
|
|
106
|
+
fi
|
|
107
|
+
if [ -e "$receipt" ] || [ -e "$node" ] || [ -L "$node" ]; then
|
|
108
|
+
refuse "its bundled Node ($node) is missing, incomplete or changed since it was verified, and there is no node on PATH" "$repair"
|
|
109
|
+
fi
|
|
110
|
+
refuse "its bundled Node is not installed and there is no node on PATH" "$repair" 127
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #1450 C1: may this install of @songsid/agend proceed? Called by the postinstall, inside npm's transaction (a refusal
|
|
3
|
+
// makes npm roll the install back). The lock is the updater's (src/install-lock.ts): `<prefix>/.agend-install.lock`,
|
|
4
|
+
// next to the package it guards. The prefix comes from WHERE this package is — never from the environment.
|
|
5
|
+
// - no lock, no token → a plain external install: proceed (the documented restriction covers the rest);
|
|
6
|
+
// - a live lock, its own token → the updater's own npm child: proceed;
|
|
7
|
+
// - a live lock, other/no token → a foreign install while AgEnD owns the prefix: refuse;
|
|
8
|
+
// - a token, but no live lock → late or stale (it cannot authorise a successor): refuse;
|
|
9
|
+
// - a lock that cannot be read → refuse, naming the file (unknown ownership blocks).
|
|
10
|
+
// Same syntax constraints as runtime-platform.cjs: this may run on an old Node.
|
|
11
|
+
var fs = require("fs");
|
|
12
|
+
var path = require("path");
|
|
13
|
+
var childProcess = require("child_process");
|
|
14
|
+
|
|
15
|
+
var LOCK = ".agend-install.lock";
|
|
16
|
+
var TOKEN_ENV = "AGEND_INSTALL_TOKEN";
|
|
17
|
+
|
|
18
|
+
/** `<prefix>/lib/node_modules/@songsid/agend` → `<prefix>`; null for any other layout (a local install, a checkout). */
|
|
19
|
+
function globalPrefix(pkgDir) {
|
|
20
|
+
var scope = path.dirname(pkgDir);
|
|
21
|
+
var nodeModules = path.dirname(scope);
|
|
22
|
+
var lib = path.dirname(nodeModules);
|
|
23
|
+
if (path.basename(pkgDir) !== "agend" || path.basename(scope) !== "@songsid" || path.basename(nodeModules) !== "node_modules" || path.basename(lib) !== "lib") return null;
|
|
24
|
+
return path.dirname(lib);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** ps by absolute path when it is where the OS keeps it: a service's or a test's PATH may not reach it. */
|
|
28
|
+
var PS = ["/bin/ps", "/usr/bin/ps"].filter(function (p) { try { fs.accessSync(p, fs.constants.X_OK); return true; } catch (e) { return false; } })[0] || "ps";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A process's start time as one canonical text: `ps -o lstart=` under LC_ALL=C and TZ=UTC, so every caller — whatever
|
|
32
|
+
* its own locale or time zone — reads the same text for the same process. null: it could not be read.
|
|
33
|
+
*/
|
|
34
|
+
function processStart(pid) {
|
|
35
|
+
try {
|
|
36
|
+
var env = {};
|
|
37
|
+
Object.keys(process.env).forEach(function (k) { env[k] = process.env[k]; });
|
|
38
|
+
env.LC_ALL = "C";
|
|
39
|
+
env.TZ = "UTC";
|
|
40
|
+
var r = childProcess.spawnSync(PS, ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8", timeout: 5000, env: env, stdio: ["ignore", "pipe", "ignore"] });
|
|
41
|
+
var text = String(r.stdout || "").trim();
|
|
42
|
+
return r.status === 0 && text ? text : null;
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The record, exactly as the updater validates it (src/install-lock.ts parseInstallLock), or null. */
|
|
49
|
+
/** Does `pid` exist? Only a definite "no such process" is no. */
|
|
50
|
+
function exists(pid) {
|
|
51
|
+
try { process.kill(pid, 0); return true; } catch (e) { return e.code !== "ESRCH"; }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The lock holder's state: "live" (that very process), "stale" (PROVEN gone: no such pid, or the pid is now a process
|
|
56
|
+
* that started at another time), or "unknown" (it exists but its start time cannot be read) — which blocks, never
|
|
57
|
+
* counts as stale. deps (tests): exists, processStart.
|
|
58
|
+
*/
|
|
59
|
+
function holderState(lock, deps) {
|
|
60
|
+
var d = deps || {};
|
|
61
|
+
if (!(d.exists || exists)(lock.pid)) return "stale";
|
|
62
|
+
var seen = (d.processStart || processStart)(lock.pid);
|
|
63
|
+
if (seen === null || seen === undefined) return "unknown";
|
|
64
|
+
return seen === lock.processStart ? "live" : "stale";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parse(text) {
|
|
68
|
+
var r;
|
|
69
|
+
try { r = JSON.parse(text); } catch (e) { return null; }
|
|
70
|
+
return r && typeof r === "object" && Number.isSafeInteger(r.pid) && r.pid > 0 && typeof r.processStart === "string" && r.processStart
|
|
71
|
+
&& typeof r.prefix === "string" && typeof r.token === "string" && /^[0-9a-f]{32,}$/.test(r.token)
|
|
72
|
+
&& typeof r.targetSpec === "string" && typeof r.agendHome === "string" && typeof r.createdAt === "string" ? r : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** { ok: true, why } or { ok: false, reason }. deps (tests): env, processStart. */
|
|
76
|
+
function admit(pkgDir, deps) {
|
|
77
|
+
var d = deps || {};
|
|
78
|
+
var env = d.env || process.env;
|
|
79
|
+
var token = env[TOKEN_ENV] || "";
|
|
80
|
+
var real;
|
|
81
|
+
try { real = fs.realpathSync(pkgDir); } catch (e) { real = pkgDir; }
|
|
82
|
+
var prefix = globalPrefix(real);
|
|
83
|
+
if (!prefix) return token ? { ok: false, reason: "an AgEnD install token was presented, but this is not a global install it could belong to" } : { ok: true, why: "not a global install" };
|
|
84
|
+
try { prefix = fs.realpathSync(prefix); } catch (e) { /* as derived */ }
|
|
85
|
+
var lockPath = path.join(prefix, LOCK);
|
|
86
|
+
var text = null;
|
|
87
|
+
try { text = fs.readFileSync(lockPath, "utf8"); } catch (e) {
|
|
88
|
+
if (e.code !== "ENOENT") return { ok: false, reason: "the install lock " + lockPath + " cannot be read (" + e.code + ")" };
|
|
89
|
+
}
|
|
90
|
+
if (text === null) {
|
|
91
|
+
return token ? { ok: false, reason: "an AgEnD install token was presented, but no update holds " + lockPath + " (a late or stale token)" } : { ok: true, why: "no lock: an external install" };
|
|
92
|
+
}
|
|
93
|
+
var lock = parse(text);
|
|
94
|
+
if (!lock) return { ok: false, reason: "the install lock " + lockPath + " cannot be read as a lock; if no `agend update` is running, remove it and retry" };
|
|
95
|
+
var state = holderState(lock, d);
|
|
96
|
+
if (state === "unknown") return { ok: false, reason: "whether the AgEnD update that holds " + lockPath + " (pid " + lock.pid + ") is still running cannot be told; retry" };
|
|
97
|
+
if (state === "stale") {
|
|
98
|
+
return token ? { ok: false, reason: "the AgEnD update that held " + lockPath + " is gone, so its token cannot authorise this install" } : { ok: true, why: "a stale lock and no token: an external install" };
|
|
99
|
+
}
|
|
100
|
+
if (lock.prefix !== prefix) return { ok: false, reason: "the install lock " + lockPath + " names another prefix (" + lock.prefix + ")" };
|
|
101
|
+
if (token && token === lock.token) return { ok: true, why: "the updater's own install" };
|
|
102
|
+
return { ok: false, reason: "an `agend update` (pid " + lock.pid + ") is installing into " + prefix + " right now; wait for it, then retry" };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = { LOCK: LOCK, TOKEN_ENV: TOKEN_ENV, admit: admit, globalPrefix: globalPrefix, processStart: processStart, exists: exists, holderState: holderState, parse: parse };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #1450 C2: the launcher body shared by the `agend` and `agend-agent` bins. It picks the interpreter BEFORE any module
|
|
3
|
+
// of the CLI's import graph loads, never downloads, and never changes PATH. Syntax old Nodes still parse.
|
|
4
|
+
var fs = require("fs");
|
|
5
|
+
var path = require("path");
|
|
6
|
+
var childProcess = require("child_process");
|
|
7
|
+
var select = require("./runtime-select.cjs");
|
|
8
|
+
|
|
9
|
+
var FORWARDED = ["SIGTERM", "SIGHUP", "SIGUSR1", "SIGUSR2"];
|
|
10
|
+
|
|
11
|
+
function realpath(file) {
|
|
12
|
+
try { return fs.realpathSync(file); } catch (e) { return null; }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** `entryRelative`: the CLI inside the package, e.g. "dist/cli.js". */
|
|
16
|
+
module.exports = function launch(entryRelative, launcherDir) {
|
|
17
|
+
var args = process.argv.slice(2);
|
|
18
|
+
var chosen = select.selectRuntime(launcherDir);
|
|
19
|
+
|
|
20
|
+
// For the updater (C4): which Node and package would this install run on? Printed, nothing started.
|
|
21
|
+
if (args[0] === "--agend-select-json") {
|
|
22
|
+
process.stdout.write(JSON.stringify(chosen.ok
|
|
23
|
+
? { node: chosen.node, source: chosen.source, pkgDir: chosen.pkgDir, entry: path.join(chosen.pkgDir, entryRelative), warning: chosen.warning || null }
|
|
24
|
+
: { error: chosen.reason, recovery: chosen.recovery }) + "\n");
|
|
25
|
+
process.exit(chosen.ok ? 0 : 1);
|
|
26
|
+
}
|
|
27
|
+
if (!chosen.ok) {
|
|
28
|
+
process.stderr.write("\n AgEnD cannot start: " + chosen.reason + ".\n To repair: " + chosen.recovery + "\n\n");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
if (chosen.warning) process.stderr.write(" ⚠ AgEnD: " + chosen.warning + "\n");
|
|
32
|
+
|
|
33
|
+
var entry = path.join(chosen.pkgDir, entryRelative);
|
|
34
|
+
if (chosen.node === realpath(process.execPath)) {
|
|
35
|
+
require("./run-in-process.cjs")(entry);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var env = {};
|
|
40
|
+
Object.keys(process.env).forEach(function (key) { env[key] = process.env[key]; });
|
|
41
|
+
env.AGEND_NODE_SELECTED = "1";
|
|
42
|
+
// The handlers are in place BEFORE the spawn: a signal delivered between spawn() and their registration would kill
|
|
43
|
+
// this launcher by default and orphan the CLI. (JS handlers cannot run during this synchronous block, so `child` is
|
|
44
|
+
// always set by the time one does.)
|
|
45
|
+
var child = null;
|
|
46
|
+
FORWARDED.forEach(function (signal) {
|
|
47
|
+
process.on(signal, function () { try { child.kill(signal); } catch (e) { /* gone */ } });
|
|
48
|
+
});
|
|
49
|
+
// A terminal's Ctrl+C reaches the whole process group, the child included: the child decides, this waits.
|
|
50
|
+
process.on("SIGINT", function () {});
|
|
51
|
+
child = childProcess.spawn(chosen.node, [entry].concat(args), { stdio: "inherit", env: env });
|
|
52
|
+
child.on("error", function (err) {
|
|
53
|
+
process.stderr.write("\n AgEnD cannot start " + chosen.node + ": " + err.message + "\n\n");
|
|
54
|
+
process.exit(1);
|
|
55
|
+
});
|
|
56
|
+
child.on("exit", function (code, signal) {
|
|
57
|
+
if (signal) {
|
|
58
|
+
process.removeAllListeners(signal);
|
|
59
|
+
process.kill(process.pid, signal);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
process.exit(code === null ? 1 : code);
|
|
63
|
+
});
|
|
64
|
+
};
|