shioricode 0.5.1 → 0.5.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/README.md +22 -1
- package/dist/bin.cjs +386 -142
- package/dist/bin.mjs +386 -142
- package/dist/client/assets/{ArtifactPanel-1Tg5ZLm5.js → ArtifactPanel-ClA97FO8.js} +2 -2
- package/dist/client/assets/{ChatMarkdown-BvwoXC6z.js → ChatMarkdown-DkldD1_B.js} +2 -2
- package/dist/client/assets/{DiffPanel-D-xp36wD.js → DiffPanel-BYSH-G7M.js} +3 -3
- package/dist/client/assets/{DiffPanelShell-DdBr4p9c.js → DiffPanelShell-Ce2wOE9L.js} +2 -2
- package/dist/client/assets/{DiffWorkerPoolProvider--93ReMXi.js → DiffWorkerPoolProvider-DjSdGkRQ.js} +2 -2
- package/dist/client/assets/{NoActiveThreadState-B_C5Y_aO.js → NoActiveThreadState-CVDfDuF2.js} +2 -2
- package/dist/client/assets/{ProviderModelPicker-BWTkQW8f.js → ProviderModelPicker-BFlX8lZL.js} +2 -2
- package/dist/client/assets/SettingsPanels-_8_YaXgc.js +4 -0
- package/dist/client/assets/{_chat-BhqvWJHj.js → _chat-CQ5UewiP.js} +2 -2
- package/dist/client/assets/_chat._threadId-5846z-_l.js +289 -0
- package/dist/client/assets/{_chat.index-oZTnMcB3.js → _chat.index-DI7egWLc.js} +2 -2
- package/dist/client/assets/automations-D6SQKJ83.js +2 -0
- package/dist/client/assets/{badge-BkzSFcY-.js → badge-iReKY3O4.js} +2 -2
- package/dist/client/assets/{clientSettings--1UP2ayD.js → clientSettings-DFgmWajw.js} +2 -2
- package/dist/client/assets/{collapsible-CqRg_DyH.js → collapsible-BEW6Xu4N.js} +2 -2
- package/dist/client/assets/{dialog-C7Ppq2qI.js → dialog-CZ2IjObB.js} +2 -2
- package/dist/client/assets/{gitReactQuery-BBOGuHot.js → gitReactQuery-BxToLb49.js} +2 -2
- package/dist/client/assets/{index-DvBh1mNh.js → index-B42nGfCf.js} +5 -5
- package/dist/client/assets/{input-emuaAuIe.js → input-CthhcRBE.js} +2 -2
- package/dist/client/assets/pull-requests-DGa0NhIZ.js +46 -0
- package/dist/client/assets/{queuedTurnsStore-I4LoXHLF.js → queuedTurnsStore-CYufNsaQ.js} +2 -2
- package/dist/client/assets/{scroll-area-kK2XF1gm.js → scroll-area-D6P_hAXV.js} +2 -2
- package/dist/client/assets/{separator-CSqq2xHm.js → separator-ByukCTaZ.js} +2 -2
- package/dist/client/assets/{settings-C9R1j3GG.js → settings-BwPioqTL.js} +2 -2
- package/dist/client/assets/{settings.appearance-Cac_fbGk.js → settings.appearance-DxQzPuf5.js} +2 -2
- package/dist/client/assets/settings.archived-BSqIxOKY.js +2 -0
- package/dist/client/assets/{settings.computer-use-BL801Zu5.js → settings.computer-use-DuuUffvc.js} +2 -2
- package/dist/client/assets/{settings.credits-Brx-cGXe.js → settings.credits-DiXAbJQa.js} +2 -2
- package/dist/client/assets/settings.general-DjIbwGSI.js +2 -0
- package/dist/client/assets/{settings.mcp-DEMm4v5q.js → settings.mcp-CHZOA1L-.js} +2 -2
- package/dist/client/assets/{settings.mobile-DF1raoba.js → settings.mobile-D3UiSzxp.js} +2 -2
- package/dist/client/assets/{settings.plugins-CewrpoKP.js → settings.plugins-qo7Fls2x.js} +2 -2
- package/dist/client/assets/{settings.remote-BufK_lG_.js → settings.remote-CmdhJhKY.js} +2 -2
- package/dist/client/assets/{settings.skills-Cz9xy1ba.js → settings.skills-UB5Wtkl5.js} +2 -2
- package/dist/client/assets/{settings.theme-builder-BU_DRAvo.js → settings.theme-builder-CnwNYRga.js} +2 -2
- package/dist/client/assets/{skillsMcpShared-DxxY1mEt.js → skillsMcpShared-CiunLdEL.js} +2 -2
- package/dist/client/assets/{store-BQMb1nBV.js → store-LT9wKN7R.js} +3 -3
- package/dist/client/assets/{terminalStateStore-C38k-Eaw.js → terminalStateStore-B-V7Awi_.js} +2 -2
- package/dist/client/assets/{textarea-DXuuOfmM.js → textarea-DAzODx1v.js} +2 -2
- package/dist/client/assets/{toggle-group-CvAnsecf.js → toggle-group-CMWeXyeK.js} +2 -2
- package/dist/client/assets/{toggle-nUm5N7vp.js → toggle-uzFsauJb.js} +2 -2
- package/dist/client/assets/{uiStateStore-Di3ieZaJ.js → uiStateStore-CgvPrTF8.js} +2 -2
- package/dist/client/assets/{useHandleNewThread-WYMZPumx.js → useHandleNewThread-Chs2blac.js} +2 -2
- package/dist/client/assets/{useSettings-DOuHg9jg.js → useSettings-CaigvboL.js} +2 -2
- package/dist/client/assets/{useTheme-Dhz9U3on.js → useTheme-7QjQMsSZ.js} +2 -2
- package/dist/client/assets/{vendor-ui-DmvKhMzf.js → vendor-ui-Ds9VgrYb.js} +2 -2
- package/dist/client/index.html +12 -12
- package/package.json +1 -1
- package/dist/client/assets/SettingsPanels-DGYlfoWv.js +0 -4
- package/dist/client/assets/_chat._threadId-Blwb3CRP.js +0 -289
- package/dist/client/assets/automations-ChGelqHO.js +0 -2
- package/dist/client/assets/pull-requests-xKFO2G81.js +0 -46
- package/dist/client/assets/settings.archived-KRU4XARw.js +0 -2
- package/dist/client/assets/settings.general-BXcV60tY.js +0 -2
package/dist/bin.mjs
CHANGED
|
@@ -72447,42 +72447,112 @@ var RemoteStateStore = class {
|
|
|
72447
72447
|
const execFile$5 = promisify(execFile);
|
|
72448
72448
|
const SERVICE_PORT = 3773;
|
|
72449
72449
|
const RECOVERY_USERNAME = "recovery";
|
|
72450
|
+
const SERVICE_LAYOUT_VERSION = 1;
|
|
72451
|
+
function invokingUsername() {
|
|
72452
|
+
const sudoUser = process.env.SUDO_USER?.trim();
|
|
72453
|
+
if (sudoUser && sudoUser !== "root") return sudoUser;
|
|
72454
|
+
return os.userInfo().username;
|
|
72455
|
+
}
|
|
72456
|
+
function invokingUid() {
|
|
72457
|
+
const sudoUid = Number(process.env.SUDO_UID);
|
|
72458
|
+
if (Number.isInteger(sudoUid) && sudoUid >= 0) return sudoUid;
|
|
72459
|
+
return typeof process.getuid === "function" ? process.getuid() : 0;
|
|
72460
|
+
}
|
|
72461
|
+
function defaultAccount(platform) {
|
|
72462
|
+
if (platform === "darwin") return "_shioricode";
|
|
72463
|
+
if (platform === "linux") return "shioricode";
|
|
72464
|
+
return "ShioriCode";
|
|
72465
|
+
}
|
|
72466
|
+
function defaultExistingHomeDir(platform, account) {
|
|
72467
|
+
if (account === os.userInfo().username) return os.homedir();
|
|
72468
|
+
if (platform === "darwin") return path.join("/Users", account);
|
|
72469
|
+
if (platform === "linux") return account === "root" ? "/root" : path.join("/home", account);
|
|
72470
|
+
return process.env.USERPROFILE || os.homedir();
|
|
72471
|
+
}
|
|
72472
|
+
function defaultServicePath(platform) {
|
|
72473
|
+
if (platform === "darwin") return "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
72474
|
+
if (platform === "linux") return "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
72475
|
+
return process.env.Path ?? process.env.PATH ?? "";
|
|
72476
|
+
}
|
|
72477
|
+
function validateServiceAccount(account) {
|
|
72478
|
+
if (!/^[A-Za-z_][A-Za-z0-9._-]*\$?$/.test(account)) throw new Error(`Invalid service username: ${account}`);
|
|
72479
|
+
}
|
|
72480
|
+
function validateServiceLayout(layout) {
|
|
72481
|
+
const isAbsolute = layout.platform === "win32" ? path.win32.isAbsolute : path.isAbsolute;
|
|
72482
|
+
for (const [label, value] of [
|
|
72483
|
+
["home directory", layout.homeDir],
|
|
72484
|
+
["state directory", layout.stateDir],
|
|
72485
|
+
["workspace directory", layout.workspaceDir],
|
|
72486
|
+
["service definition", layout.definitionPath],
|
|
72487
|
+
["log file", layout.logPath]
|
|
72488
|
+
]) if (!isAbsolute(value) || /[\0\r\n]/.test(value)) throw new Error(`The ${label} must be an absolute path without control characters`);
|
|
72489
|
+
if (/\0|\r|\n/.test(layout.servicePath)) throw new Error("The service PATH cannot contain control characters");
|
|
72490
|
+
if (!Number.isInteger(layout.port) || layout.port < 1 || layout.port > 65535) throw new Error(`Invalid service port: ${layout.port}`);
|
|
72491
|
+
return layout;
|
|
72492
|
+
}
|
|
72450
72493
|
function supportedPlatform(platform) {
|
|
72451
72494
|
if (platform === "linux" || platform === "darwin" || platform === "win32") return platform;
|
|
72452
72495
|
throw new Error(`ShioriCode services are not supported on ${platform} yet`);
|
|
72453
72496
|
}
|
|
72454
|
-
function serviceLayout(platform) {
|
|
72497
|
+
function serviceLayout(platform, options = {}) {
|
|
72498
|
+
const accountMode = options.accountMode ?? "dedicated";
|
|
72499
|
+
const account = options.account?.trim() || (accountMode === "current" ? invokingUsername() : defaultAccount(platform));
|
|
72500
|
+
validateServiceAccount(account);
|
|
72455
72501
|
switch (platform) {
|
|
72456
|
-
case "linux":
|
|
72457
|
-
|
|
72458
|
-
|
|
72459
|
-
stateDir: "
|
|
72460
|
-
|
|
72461
|
-
|
|
72462
|
-
|
|
72463
|
-
|
|
72464
|
-
|
|
72465
|
-
|
|
72466
|
-
|
|
72467
|
-
|
|
72468
|
-
|
|
72469
|
-
|
|
72470
|
-
|
|
72471
|
-
|
|
72472
|
-
|
|
72473
|
-
}
|
|
72502
|
+
case "linux": {
|
|
72503
|
+
const dedicatedStateDir = "/var/lib/shioricode";
|
|
72504
|
+
const homeDir = options.homeDir ?? (accountMode === "dedicated" ? dedicatedStateDir : defaultExistingHomeDir(platform, account));
|
|
72505
|
+
const stateDir = options.stateDir ?? (accountMode === "dedicated" ? dedicatedStateDir : path.join(homeDir, ".shioricode-service"));
|
|
72506
|
+
return validateServiceLayout({
|
|
72507
|
+
platform,
|
|
72508
|
+
accountMode,
|
|
72509
|
+
account,
|
|
72510
|
+
homeDir,
|
|
72511
|
+
stateDir,
|
|
72512
|
+
workspaceDir: options.workspaceDir ?? path.join(stateDir, "workspaces"),
|
|
72513
|
+
definitionPath: accountMode === "dedicated" ? "/etc/systemd/system/shioricode.service" : path.join(homeDir, ".config/systemd/user/shioricode.service"),
|
|
72514
|
+
logPath: options.logPath ?? (accountMode === "dedicated" ? "/var/log/shioricode/server.log" : path.join(stateDir, "server.log")),
|
|
72515
|
+
servicePath: options.servicePath ?? defaultServicePath(platform),
|
|
72516
|
+
serviceId: "shioricode.service",
|
|
72517
|
+
port: options.port ?? SERVICE_PORT
|
|
72518
|
+
});
|
|
72519
|
+
}
|
|
72520
|
+
case "darwin": {
|
|
72521
|
+
const dedicatedStateDir = "/Library/Application Support/ShioriCode";
|
|
72522
|
+
const homeDir = options.homeDir ?? (accountMode === "dedicated" ? dedicatedStateDir : defaultExistingHomeDir(platform, account));
|
|
72523
|
+
const stateDir = options.stateDir ?? (accountMode === "dedicated" ? dedicatedStateDir : path.join(homeDir, ".shioricode-service"));
|
|
72524
|
+
return validateServiceLayout({
|
|
72525
|
+
platform,
|
|
72526
|
+
accountMode,
|
|
72527
|
+
account,
|
|
72528
|
+
homeDir,
|
|
72529
|
+
stateDir,
|
|
72530
|
+
workspaceDir: options.workspaceDir ?? (accountMode === "dedicated" ? "/Users/Shared/ShioriCode/Workspaces" : path.join(stateDir, "workspaces")),
|
|
72531
|
+
definitionPath: accountMode === "dedicated" ? "/Library/LaunchDaemons/codes.shiori.shioricode.plist" : path.join(homeDir, "Library/LaunchAgents/codes.shiori.shioricode.plist"),
|
|
72532
|
+
logPath: options.logPath ?? (accountMode === "dedicated" ? "/Library/Logs/ShioriCode/server.log" : path.join(stateDir, "server.log")),
|
|
72533
|
+
servicePath: options.servicePath ?? defaultServicePath(platform),
|
|
72534
|
+
serviceId: "codes.shiori.shioricode",
|
|
72535
|
+
port: options.port ?? SERVICE_PORT
|
|
72536
|
+
});
|
|
72537
|
+
}
|
|
72474
72538
|
case "win32": {
|
|
72475
72539
|
const programData = process.env.ProgramData || "C:\\ProgramData";
|
|
72476
|
-
const
|
|
72477
|
-
|
|
72540
|
+
const dedicatedStateDir = path.win32.join(programData, "ShioriCode");
|
|
72541
|
+
const homeDir = options.homeDir ?? (accountMode === "dedicated" ? dedicatedStateDir : defaultExistingHomeDir(platform, account));
|
|
72542
|
+
const stateDir = options.stateDir ?? (accountMode === "dedicated" ? dedicatedStateDir : path.win32.join(homeDir, ".shioricode-service"));
|
|
72543
|
+
return validateServiceLayout({
|
|
72478
72544
|
platform,
|
|
72479
|
-
|
|
72545
|
+
accountMode,
|
|
72546
|
+
account,
|
|
72547
|
+
homeDir,
|
|
72480
72548
|
stateDir,
|
|
72481
|
-
workspaceDir: path.win32.join(stateDir, "workspaces"),
|
|
72549
|
+
workspaceDir: options.workspaceDir ?? path.win32.join(stateDir, "workspaces"),
|
|
72482
72550
|
definitionPath: path.win32.join(stateDir, "service.cmd"),
|
|
72483
|
-
logPath: path.win32.join(stateDir, "server.log"),
|
|
72484
|
-
|
|
72485
|
-
|
|
72551
|
+
logPath: options.logPath ?? path.win32.join(stateDir, "server.log"),
|
|
72552
|
+
servicePath: options.servicePath ?? defaultServicePath(platform),
|
|
72553
|
+
serviceId: "ShioriCode",
|
|
72554
|
+
port: options.port ?? SERVICE_PORT
|
|
72555
|
+
});
|
|
72486
72556
|
}
|
|
72487
72557
|
}
|
|
72488
72558
|
}
|
|
@@ -72495,20 +72565,21 @@ function xmlEscape(value) {
|
|
|
72495
72565
|
function windowsBatchQuote(value) {
|
|
72496
72566
|
return `"${value.replaceAll("\"", "\"\"")}"`;
|
|
72497
72567
|
}
|
|
72498
|
-
function serviceArguments(layout) {
|
|
72499
|
-
|
|
72568
|
+
function serviceArguments(layout, requireAuth) {
|
|
72569
|
+
const args = [
|
|
72500
72570
|
"serve",
|
|
72501
72571
|
"--mode",
|
|
72502
72572
|
"web",
|
|
72503
72573
|
"--host",
|
|
72504
72574
|
"127.0.0.1",
|
|
72505
72575
|
"--port",
|
|
72506
|
-
String(
|
|
72576
|
+
String(layout.port),
|
|
72507
72577
|
"--base-dir",
|
|
72508
72578
|
layout.stateDir,
|
|
72509
|
-
"--remote",
|
|
72510
72579
|
"--no-browser"
|
|
72511
72580
|
];
|
|
72581
|
+
if (requireAuth) args.splice(-1, 0, "--remote");
|
|
72582
|
+
return args;
|
|
72512
72583
|
}
|
|
72513
72584
|
function serviceRuntimeRoot(layout) {
|
|
72514
72585
|
return path.join(layout.stateDir, "runtime");
|
|
@@ -72521,7 +72592,6 @@ function stageServiceRuntime(deps, layout) {
|
|
|
72521
72592
|
const runtimeRoot = serviceRuntimeRoot(layout);
|
|
72522
72593
|
const runtimeDir = path.join(runtimeRoot, `${Date.now()}-${randomBytes(6).toString("hex")}`);
|
|
72523
72594
|
const packageTarget = path.join(runtimeDir, "package");
|
|
72524
|
-
const nodeTarget = path.join(runtimeDir, layout.platform === "win32" ? "node.exe" : "node");
|
|
72525
72595
|
fs.mkdirSync(runtimeDir, {
|
|
72526
72596
|
recursive: true,
|
|
72527
72597
|
mode: 488
|
|
@@ -72531,8 +72601,6 @@ function stageServiceRuntime(deps, layout) {
|
|
|
72531
72601
|
recursive: true,
|
|
72532
72602
|
dereference: true
|
|
72533
72603
|
});
|
|
72534
|
-
fs.copyFileSync(deps.execPath, nodeTarget, fs.constants.COPYFILE_FICLONE);
|
|
72535
|
-
if (layout.platform !== "win32") fs.chmodSync(nodeTarget, 493);
|
|
72536
72604
|
} catch (error) {
|
|
72537
72605
|
fs.rmSync(runtimeDir, {
|
|
72538
72606
|
recursive: true,
|
|
@@ -72544,7 +72612,6 @@ function stageServiceRuntime(deps, layout) {
|
|
|
72544
72612
|
runtimeDir,
|
|
72545
72613
|
dependencies: {
|
|
72546
72614
|
...deps,
|
|
72547
|
-
execPath: nodeTarget,
|
|
72548
72615
|
cliPath: path.join(packageTarget, "dist", "bin.mjs")
|
|
72549
72616
|
}
|
|
72550
72617
|
};
|
|
@@ -72569,6 +72636,14 @@ function pruneStagedServiceRuntimes(layout, activeRuntimeDir) {
|
|
|
72569
72636
|
}
|
|
72570
72637
|
}
|
|
72571
72638
|
async function protectStagedServiceRuntime(deps, layout) {
|
|
72639
|
+
if (layout.accountMode === "current") {
|
|
72640
|
+
if (layout.platform !== "win32") await deps.run("chmod", [
|
|
72641
|
+
"-R",
|
|
72642
|
+
"u+rX",
|
|
72643
|
+
serviceRuntimeRoot(layout)
|
|
72644
|
+
]);
|
|
72645
|
+
return;
|
|
72646
|
+
}
|
|
72572
72647
|
const runtimeRoot = serviceRuntimeRoot(layout);
|
|
72573
72648
|
if (layout.platform === "win32") {
|
|
72574
72649
|
await deps.run("icacls.exe", [
|
|
@@ -72594,11 +72669,14 @@ async function protectStagedServiceRuntime(deps, layout) {
|
|
|
72594
72669
|
}
|
|
72595
72670
|
function renderSystemdUnit(input) {
|
|
72596
72671
|
const { layout } = input;
|
|
72672
|
+
const hasRecoveryLogin = input.recoveryUsername !== null && input.recoveryPassword !== null;
|
|
72597
72673
|
const command = [
|
|
72598
72674
|
input.execPath,
|
|
72599
72675
|
input.cliPath,
|
|
72600
|
-
...serviceArguments(layout)
|
|
72676
|
+
...serviceArguments(layout, hasRecoveryLogin)
|
|
72601
72677
|
].map(systemdQuote).join(" ");
|
|
72678
|
+
const account = layout.accountMode === "dedicated" ? `User=${layout.account}\nGroup=${layout.account}\n` : "";
|
|
72679
|
+
const recoveryLogin = hasRecoveryLogin ? `Environment=${systemdQuote(`SHIORICODE_USERNAME=${input.recoveryUsername}`)}\nEnvironment=${systemdQuote(`SHIORICODE_PASSWORD=${input.recoveryPassword}`)}\n` : "";
|
|
72602
72680
|
return `[Unit]
|
|
72603
72681
|
Description=ShioriCode headless server
|
|
72604
72682
|
After=network-online.target
|
|
@@ -72606,45 +72684,40 @@ Wants=network-online.target
|
|
|
72606
72684
|
|
|
72607
72685
|
[Service]
|
|
72608
72686
|
Type=simple
|
|
72609
|
-
|
|
72610
|
-
|
|
72611
|
-
WorkingDirectory=${layout.workspaceDir}
|
|
72612
|
-
Environment=HOME=${layout.stateDir}
|
|
72687
|
+
${account}WorkingDirectory=${systemdQuote(layout.workspaceDir)}
|
|
72688
|
+
Environment=${systemdQuote(`HOME=${layout.homeDir}`)}
|
|
72613
72689
|
Environment=NODE_ENV=production
|
|
72614
72690
|
Environment=SHIORICODE_SERVICE=1
|
|
72615
72691
|
Environment=SHIORICODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD=false
|
|
72616
|
-
Environment
|
|
72617
|
-
|
|
72618
|
-
Environment=SHIORICODE_PASSWORD=${input.recoveryPassword}
|
|
72619
|
-
ExecStart=${command}
|
|
72692
|
+
Environment=${systemdQuote(`PATH=${layout.servicePath}`)}
|
|
72693
|
+
${recoveryLogin}ExecStart=${command}
|
|
72620
72694
|
Restart=on-failure
|
|
72621
72695
|
RestartSec=3
|
|
72622
72696
|
SuccessExitStatus=130 143
|
|
72623
72697
|
LimitNOFILE=65535
|
|
72624
|
-
StandardOutput
|
|
72625
|
-
StandardError
|
|
72698
|
+
StandardOutput=${systemdQuote(`append:${layout.logPath}`)}
|
|
72699
|
+
StandardError=${systemdQuote(`append:${layout.logPath}`)}
|
|
72626
72700
|
|
|
72627
72701
|
[Install]
|
|
72628
72702
|
WantedBy=multi-user.target
|
|
72629
72703
|
`;
|
|
72630
72704
|
}
|
|
72631
72705
|
function renderLaunchDaemon(input) {
|
|
72706
|
+
const hasRecoveryLogin = input.recoveryUsername !== null && input.recoveryPassword !== null;
|
|
72632
72707
|
const args = [
|
|
72633
72708
|
input.execPath,
|
|
72634
72709
|
input.cliPath,
|
|
72635
|
-
...serviceArguments(input.layout)
|
|
72710
|
+
...serviceArguments(input.layout, hasRecoveryLogin)
|
|
72636
72711
|
].map((value) => ` <string>${xmlEscape(value)}</string>`).join("\n");
|
|
72712
|
+
const account = input.layout.accountMode === "dedicated" ? ` <key>UserName</key>\n <string>${input.layout.account}</string>\n <key>GroupName</key>\n <string>${input.layout.account}</string>\n` : "";
|
|
72713
|
+
const recoveryLogin = hasRecoveryLogin ? ` <key>SHIORICODE_USERNAME</key><string>${xmlEscape(input.recoveryUsername)}</string>\n <key>SHIORICODE_PASSWORD</key><string>${xmlEscape(input.recoveryPassword)}</string>\n` : "";
|
|
72637
72714
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
72638
72715
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
72639
72716
|
<plist version="1.0">
|
|
72640
72717
|
<dict>
|
|
72641
72718
|
<key>Label</key>
|
|
72642
72719
|
<string>${input.layout.serviceId}</string>
|
|
72643
|
-
<key>
|
|
72644
|
-
<string>${input.layout.account}</string>
|
|
72645
|
-
<key>GroupName</key>
|
|
72646
|
-
<string>${input.layout.account}</string>
|
|
72647
|
-
<key>WorkingDirectory</key>
|
|
72720
|
+
${account} <key>WorkingDirectory</key>
|
|
72648
72721
|
<string>${xmlEscape(input.layout.workspaceDir)}</string>
|
|
72649
72722
|
<key>ProgramArguments</key>
|
|
72650
72723
|
<array>
|
|
@@ -72652,14 +72725,12 @@ ${args}
|
|
|
72652
72725
|
</array>
|
|
72653
72726
|
<key>EnvironmentVariables</key>
|
|
72654
72727
|
<dict>
|
|
72655
|
-
<key>HOME</key><string>${xmlEscape(input.layout.
|
|
72728
|
+
<key>HOME</key><string>${xmlEscape(input.layout.homeDir)}</string>
|
|
72656
72729
|
<key>NODE_ENV</key><string>production</string>
|
|
72657
72730
|
<key>SHIORICODE_SERVICE</key><string>1</string>
|
|
72658
72731
|
<key>SHIORICODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD</key><string>false</string>
|
|
72659
|
-
<key>PATH</key><string
|
|
72660
|
-
|
|
72661
|
-
<key>SHIORICODE_PASSWORD</key><string>${xmlEscape(input.recoveryPassword)}</string>
|
|
72662
|
-
</dict>
|
|
72732
|
+
<key>PATH</key><string>${xmlEscape(input.layout.servicePath)}</string>
|
|
72733
|
+
${recoveryLogin} </dict>
|
|
72663
72734
|
<key>RunAtLoad</key><true/>
|
|
72664
72735
|
<key>KeepAlive</key><true/>
|
|
72665
72736
|
<key>ThrottleInterval</key><integer>3</integer>
|
|
@@ -72670,19 +72741,20 @@ ${args}
|
|
|
72670
72741
|
`;
|
|
72671
72742
|
}
|
|
72672
72743
|
function renderWindowsServiceScript(input) {
|
|
72744
|
+
const hasRecoveryLogin = input.recoveryUsername !== null && input.recoveryPassword !== null;
|
|
72673
72745
|
const command = [
|
|
72674
72746
|
input.execPath,
|
|
72675
72747
|
input.cliPath,
|
|
72676
|
-
...serviceArguments(input.layout)
|
|
72748
|
+
...serviceArguments(input.layout, hasRecoveryLogin)
|
|
72677
72749
|
].map(windowsBatchQuote).join(" ");
|
|
72750
|
+
const recoveryLogin = hasRecoveryLogin ? `set "SHIORICODE_USERNAME=${input.recoveryUsername}"\r\nset "SHIORICODE_PASSWORD=${input.recoveryPassword}"\r\n` : "";
|
|
72678
72751
|
return `@echo off\r
|
|
72679
|
-
set "HOME=${input.layout.
|
|
72752
|
+
set "HOME=${input.layout.homeDir}"\r
|
|
72680
72753
|
set "NODE_ENV=production"\r
|
|
72681
72754
|
set "SHIORICODE_SERVICE=1"\r
|
|
72682
72755
|
set "SHIORICODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD=false"\r
|
|
72683
|
-
set "
|
|
72684
|
-
|
|
72685
|
-
cd /d ${windowsBatchQuote(input.layout.workspaceDir)}\r
|
|
72756
|
+
set "PATH=${input.layout.servicePath}"\r
|
|
72757
|
+
${recoveryLogin}cd /d ${windowsBatchQuote(input.layout.workspaceDir)}\r
|
|
72686
72758
|
${command} >> ${windowsBatchQuote(input.layout.logPath)} 2>&1\r
|
|
72687
72759
|
`;
|
|
72688
72760
|
}
|
|
@@ -72698,11 +72770,24 @@ function defaultDependencies$2() {
|
|
|
72698
72770
|
if (!cliArg) throw new Error("Could not resolve the ShioriCode executable path");
|
|
72699
72771
|
return {
|
|
72700
72772
|
platform: supportedPlatform(process.platform),
|
|
72701
|
-
execPath:
|
|
72773
|
+
execPath: stableNodeExecutable(),
|
|
72702
72774
|
cliPath: fs.realpathSync(cliArg),
|
|
72703
72775
|
run: runDefault
|
|
72704
72776
|
};
|
|
72705
72777
|
}
|
|
72778
|
+
function stableNodeExecutable() {
|
|
72779
|
+
if (process.platform === "win32") return process.execPath;
|
|
72780
|
+
const pathEntries = (process.env.PATH ?? "").split(path.delimiter);
|
|
72781
|
+
for (const entry of pathEntries) {
|
|
72782
|
+
if (!entry) continue;
|
|
72783
|
+
const candidate = path.join(entry, "node");
|
|
72784
|
+
try {
|
|
72785
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
72786
|
+
return candidate;
|
|
72787
|
+
} catch {}
|
|
72788
|
+
}
|
|
72789
|
+
return process.execPath;
|
|
72790
|
+
}
|
|
72706
72791
|
function requireAdministrator(platform) {
|
|
72707
72792
|
if (platform !== "win32" && typeof process.getuid === "function" && process.getuid() !== 0) throw new Error("Service installation requires administrator privileges. Re-run with sudo.");
|
|
72708
72793
|
}
|
|
@@ -72715,7 +72800,9 @@ async function commandExists$1(run, file, args) {
|
|
|
72715
72800
|
}
|
|
72716
72801
|
}
|
|
72717
72802
|
async function ensureLinuxAccount(deps, layout) {
|
|
72718
|
-
|
|
72803
|
+
const accountExists = await commandExists$1(deps.run, "id", ["-u", layout.account]);
|
|
72804
|
+
if (!accountExists && layout.accountMode === "current") throw new Error(`The requested existing service user ${layout.account} does not exist`);
|
|
72805
|
+
if (!accountExists) await deps.run("useradd", [
|
|
72719
72806
|
"--system",
|
|
72720
72807
|
"--home-dir",
|
|
72721
72808
|
layout.stateDir,
|
|
@@ -72724,6 +72811,10 @@ async function ensureLinuxAccount(deps, layout) {
|
|
|
72724
72811
|
"/usr/sbin/nologin",
|
|
72725
72812
|
layout.account
|
|
72726
72813
|
]);
|
|
72814
|
+
fs.mkdirSync(layout.stateDir, {
|
|
72815
|
+
recursive: true,
|
|
72816
|
+
mode: 488
|
|
72817
|
+
});
|
|
72727
72818
|
fs.mkdirSync(layout.workspaceDir, {
|
|
72728
72819
|
recursive: true,
|
|
72729
72820
|
mode: 488
|
|
@@ -72732,14 +72823,15 @@ async function ensureLinuxAccount(deps, layout) {
|
|
|
72732
72823
|
recursive: true,
|
|
72733
72824
|
mode: 488
|
|
72734
72825
|
});
|
|
72826
|
+
if (layout.accountMode === "current") return;
|
|
72735
72827
|
await deps.run("chown", [
|
|
72736
72828
|
"-R",
|
|
72737
|
-
|
|
72829
|
+
layout.account,
|
|
72738
72830
|
layout.stateDir
|
|
72739
72831
|
]);
|
|
72740
72832
|
await deps.run("chown", [
|
|
72741
72833
|
"-R",
|
|
72742
|
-
|
|
72834
|
+
layout.account,
|
|
72743
72835
|
path.dirname(layout.logPath)
|
|
72744
72836
|
]);
|
|
72745
72837
|
}
|
|
@@ -72774,6 +72866,22 @@ async function readMacNumericAttribute(deps, record, attribute) {
|
|
|
72774
72866
|
}
|
|
72775
72867
|
}
|
|
72776
72868
|
async function ensureMacAccount(deps, layout) {
|
|
72869
|
+
if (layout.accountMode === "current") {
|
|
72870
|
+
if (!await commandExists$1(deps.run, "id", ["-u", layout.account])) throw new Error(`The requested existing service user ${layout.account} does not exist`);
|
|
72871
|
+
fs.mkdirSync(layout.stateDir, {
|
|
72872
|
+
recursive: true,
|
|
72873
|
+
mode: 488
|
|
72874
|
+
});
|
|
72875
|
+
fs.mkdirSync(layout.workspaceDir, {
|
|
72876
|
+
recursive: true,
|
|
72877
|
+
mode: 488
|
|
72878
|
+
});
|
|
72879
|
+
fs.mkdirSync(path.dirname(layout.logPath), {
|
|
72880
|
+
recursive: true,
|
|
72881
|
+
mode: 488
|
|
72882
|
+
});
|
|
72883
|
+
return;
|
|
72884
|
+
}
|
|
72777
72885
|
const userRecord = `/Users/${layout.account}`;
|
|
72778
72886
|
const groupRecord = `/Groups/${layout.account}`;
|
|
72779
72887
|
const existingUid = await readMacNumericAttribute(deps, userRecord, "UniqueID");
|
|
@@ -72883,58 +72991,80 @@ async function ensureMacAccount(deps, layout) {
|
|
|
72883
72991
|
path.dirname(layout.logPath)
|
|
72884
72992
|
]);
|
|
72885
72993
|
}
|
|
72886
|
-
async function installLinux(deps, layout, recoveryPassword) {
|
|
72994
|
+
async function installLinux(deps, layout, recoveryUsername, recoveryPassword) {
|
|
72887
72995
|
await ensureLinuxAccount(deps, layout);
|
|
72888
72996
|
const staged = stageServiceRuntime(deps, layout);
|
|
72889
72997
|
await protectStagedServiceRuntime(deps, layout);
|
|
72998
|
+
fs.mkdirSync(path.dirname(layout.definitionPath), {
|
|
72999
|
+
recursive: true,
|
|
73000
|
+
mode: 493
|
|
73001
|
+
});
|
|
72890
73002
|
fs.writeFileSync(layout.definitionPath, renderSystemdUnit({
|
|
72891
73003
|
...staged.dependencies,
|
|
72892
73004
|
layout,
|
|
73005
|
+
recoveryUsername,
|
|
72893
73006
|
recoveryPassword
|
|
72894
73007
|
}), { mode: 384 });
|
|
72895
|
-
|
|
72896
|
-
await deps.run("systemctl", ["
|
|
72897
|
-
await deps.run("systemctl", [
|
|
73008
|
+
const systemctl = layout.accountMode === "current" ? ["--user"] : [];
|
|
73009
|
+
await deps.run("systemctl", [...systemctl, "daemon-reload"]);
|
|
73010
|
+
await deps.run("systemctl", [
|
|
73011
|
+
...systemctl,
|
|
73012
|
+
"enable",
|
|
73013
|
+
layout.serviceId
|
|
73014
|
+
]);
|
|
73015
|
+
await deps.run("systemctl", [
|
|
73016
|
+
...systemctl,
|
|
73017
|
+
"restart",
|
|
73018
|
+
layout.serviceId
|
|
73019
|
+
]);
|
|
72898
73020
|
pruneStagedServiceRuntimes(layout, staged.runtimeDir);
|
|
72899
73021
|
}
|
|
72900
|
-
async function installMac(deps, layout, recoveryPassword) {
|
|
73022
|
+
async function installMac(deps, layout, recoveryUsername, recoveryPassword) {
|
|
72901
73023
|
await ensureMacAccount(deps, layout);
|
|
72902
73024
|
const staged = stageServiceRuntime(deps, layout);
|
|
72903
73025
|
await protectStagedServiceRuntime(deps, layout);
|
|
73026
|
+
fs.mkdirSync(path.dirname(layout.definitionPath), {
|
|
73027
|
+
recursive: true,
|
|
73028
|
+
mode: 493
|
|
73029
|
+
});
|
|
72904
73030
|
fs.writeFileSync(layout.definitionPath, renderLaunchDaemon({
|
|
72905
73031
|
...staged.dependencies,
|
|
72906
73032
|
layout,
|
|
73033
|
+
recoveryUsername,
|
|
72907
73034
|
recoveryPassword
|
|
72908
73035
|
}), { mode: 384 });
|
|
73036
|
+
const domain = layout.accountMode === "dedicated" ? "system" : `gui/${invokingUid()}`;
|
|
72909
73037
|
await deps.run("launchctl", [
|
|
72910
73038
|
"bootout",
|
|
72911
|
-
|
|
73039
|
+
domain,
|
|
72912
73040
|
layout.definitionPath
|
|
72913
73041
|
]).catch(() => {});
|
|
72914
73042
|
await deps.run("launchctl", [
|
|
72915
73043
|
"bootstrap",
|
|
72916
|
-
|
|
73044
|
+
domain,
|
|
72917
73045
|
layout.definitionPath
|
|
72918
73046
|
]);
|
|
72919
|
-
await deps.run("launchctl", ["enable",
|
|
73047
|
+
await deps.run("launchctl", ["enable", `${domain}/${layout.serviceId}`]);
|
|
72920
73048
|
await deps.run("launchctl", [
|
|
72921
73049
|
"kickstart",
|
|
72922
73050
|
"-k",
|
|
72923
|
-
|
|
73051
|
+
`${domain}/${layout.serviceId}`
|
|
72924
73052
|
]);
|
|
72925
73053
|
pruneStagedServiceRuntimes(layout, staged.runtimeDir);
|
|
72926
73054
|
}
|
|
72927
|
-
async function installWindows(deps, layout, recoveryPassword) {
|
|
72928
|
-
const accountPassword = randomBytes(32).toString("base64url");
|
|
72929
|
-
|
|
72930
|
-
|
|
72931
|
-
"
|
|
72932
|
-
|
|
72933
|
-
|
|
72934
|
-
|
|
72935
|
-
|
|
73055
|
+
async function installWindows(deps, layout, recoveryUsername, recoveryPassword) {
|
|
73056
|
+
const accountPassword = layout.accountMode === "dedicated" ? randomBytes(32).toString("base64url") : null;
|
|
73057
|
+
if (accountPassword) {
|
|
73058
|
+
const ps = renderWindowsAccountScript(layout.account, accountPassword);
|
|
73059
|
+
await deps.run("powershell.exe", [
|
|
73060
|
+
"-NoProfile",
|
|
73061
|
+
"-NonInteractive",
|
|
73062
|
+
"-Command",
|
|
73063
|
+
ps
|
|
73064
|
+
]);
|
|
73065
|
+
}
|
|
72936
73066
|
fs.mkdirSync(layout.workspaceDir, { recursive: true });
|
|
72937
|
-
await deps.run("icacls.exe", [
|
|
73067
|
+
if (layout.accountMode === "dedicated") await deps.run("icacls.exe", [
|
|
72938
73068
|
layout.stateDir,
|
|
72939
73069
|
"/inheritance:r",
|
|
72940
73070
|
"/grant:r",
|
|
@@ -72947,6 +73077,7 @@ async function installWindows(deps, layout, recoveryPassword) {
|
|
|
72947
73077
|
fs.writeFileSync(layout.definitionPath, renderWindowsServiceScript({
|
|
72948
73078
|
...staged.dependencies,
|
|
72949
73079
|
layout,
|
|
73080
|
+
recoveryUsername,
|
|
72950
73081
|
recoveryPassword
|
|
72951
73082
|
}), "utf8");
|
|
72952
73083
|
await deps.run("schtasks.exe", [
|
|
@@ -72954,16 +73085,23 @@ async function installWindows(deps, layout, recoveryPassword) {
|
|
|
72954
73085
|
"/TN",
|
|
72955
73086
|
layout.serviceId
|
|
72956
73087
|
]).catch(() => {});
|
|
73088
|
+
const accountArguments = accountPassword ? [
|
|
73089
|
+
"/RU",
|
|
73090
|
+
`.\\${layout.account}`,
|
|
73091
|
+
"/RP",
|
|
73092
|
+
accountPassword
|
|
73093
|
+
] : [
|
|
73094
|
+
"/RU",
|
|
73095
|
+
layout.account,
|
|
73096
|
+
"/NP"
|
|
73097
|
+
];
|
|
72957
73098
|
await deps.run("schtasks.exe", [
|
|
72958
73099
|
"/Create",
|
|
72959
73100
|
"/TN",
|
|
72960
73101
|
layout.serviceId,
|
|
72961
73102
|
"/SC",
|
|
72962
|
-
"ONSTART",
|
|
72963
|
-
|
|
72964
|
-
`.\\${layout.account}`,
|
|
72965
|
-
"/RP",
|
|
72966
|
-
accountPassword,
|
|
73103
|
+
layout.accountMode === "dedicated" ? "ONSTART" : "ONLOGON",
|
|
73104
|
+
...accountArguments,
|
|
72967
73105
|
"/TR",
|
|
72968
73106
|
layout.definitionPath,
|
|
72969
73107
|
"/F"
|
|
@@ -72987,21 +73125,90 @@ $userList='HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Spe
|
|
|
72987
73125
|
New-Item -Path $userList -Force | Out-Null
|
|
72988
73126
|
New-ItemProperty -Path $userList -Name $name -PropertyType DWord -Value 0 -Force | Out-Null`;
|
|
72989
73127
|
}
|
|
72990
|
-
|
|
73128
|
+
function systemServiceLayoutMetadataPath(platform) {
|
|
73129
|
+
if (platform === "linux") return "/etc/shioricode/service-layout.json";
|
|
73130
|
+
if (platform === "darwin") return "/Library/Preferences/codes.shiori.shioricode.service-layout.json";
|
|
73131
|
+
const programData = process.env.ProgramData || "C:\\ProgramData";
|
|
73132
|
+
return path.win32.join(programData, "ShioriCodeService", "service-layout.json");
|
|
73133
|
+
}
|
|
73134
|
+
function serviceLayoutMetadataPath(layout) {
|
|
73135
|
+
if (layout.accountMode === "dedicated") return systemServiceLayoutMetadataPath(layout.platform);
|
|
73136
|
+
const metadataHome = defaultExistingHomeDir(layout.platform, invokingUsername());
|
|
73137
|
+
if (layout.platform === "linux") return path.join(metadataHome, ".config/shioricode/service-layout.json");
|
|
73138
|
+
if (layout.platform === "darwin") return path.join(metadataHome, "Library/Application Support/ShioriCode Service/service-layout.json");
|
|
73139
|
+
return path.win32.join(process.env.LOCALAPPDATA || path.win32.join(metadataHome, "AppData", "Local"), "ShioriCodeService", "service-layout.json");
|
|
73140
|
+
}
|
|
73141
|
+
function writeServiceLayout(layout) {
|
|
73142
|
+
const metadataPath = serviceLayoutMetadataPath(layout);
|
|
73143
|
+
fs.mkdirSync(path.dirname(metadataPath), {
|
|
73144
|
+
recursive: true,
|
|
73145
|
+
mode: 493
|
|
73146
|
+
});
|
|
73147
|
+
fs.writeFileSync(metadataPath, `${JSON.stringify({
|
|
73148
|
+
version: SERVICE_LAYOUT_VERSION,
|
|
73149
|
+
layout
|
|
73150
|
+
}, null, 2)}\n`, { mode: 384 });
|
|
73151
|
+
if (layout.accountMode === "dedicated" && layout.platform !== "win32") {
|
|
73152
|
+
fs.chownSync(metadataPath, 0, 0);
|
|
73153
|
+
fs.chmodSync(metadataPath, 384);
|
|
73154
|
+
}
|
|
73155
|
+
}
|
|
73156
|
+
function readServiceLayout(metadataPath, platform) {
|
|
73157
|
+
try {
|
|
73158
|
+
const parsed = JSON.parse(fs.readFileSync(metadataPath, "utf8"));
|
|
73159
|
+
const layout = parsed.layout;
|
|
73160
|
+
if (parsed.version !== SERVICE_LAYOUT_VERSION || layout?.platform !== platform || layout.accountMode !== "dedicated" && layout.accountMode !== "current" || typeof layout.account !== "string" || typeof layout.homeDir !== "string" || typeof layout.stateDir !== "string" || typeof layout.workspaceDir !== "string" || typeof layout.definitionPath !== "string" || typeof layout.logPath !== "string" || typeof layout.servicePath !== "string" || typeof layout.serviceId !== "string" || typeof layout.port !== "number") return null;
|
|
73161
|
+
return layout;
|
|
73162
|
+
} catch {
|
|
73163
|
+
return null;
|
|
73164
|
+
}
|
|
73165
|
+
}
|
|
73166
|
+
function readInstalledServiceLayout(platform) {
|
|
73167
|
+
const currentLayout = readServiceLayout(serviceLayoutMetadataPath(serviceLayout(platform, { accountMode: "current" })), platform);
|
|
73168
|
+
if (currentLayout && fs.existsSync(currentLayout.definitionPath)) return currentLayout;
|
|
73169
|
+
const systemLayout = readServiceLayout(systemServiceLayoutMetadataPath(platform), platform);
|
|
73170
|
+
if (systemLayout && fs.existsSync(systemLayout.definitionPath)) return systemLayout;
|
|
73171
|
+
const legacy = serviceLayout(platform);
|
|
73172
|
+
return fs.existsSync(legacy.definitionPath) ? legacy : null;
|
|
73173
|
+
}
|
|
73174
|
+
function recoveryPasswordFromOptions(options) {
|
|
73175
|
+
if (!options.recoveryPasswordFile) return {
|
|
73176
|
+
password: randomBytes(32).toString("base64url"),
|
|
73177
|
+
generated: true
|
|
73178
|
+
};
|
|
73179
|
+
const password = fs.readFileSync(options.recoveryPasswordFile, "utf8").replace(/\r?\n$/, "");
|
|
73180
|
+
if (!password) throw new Error("The recovery password file is empty");
|
|
73181
|
+
if (/[\0\r\n]/.test(password)) throw new Error("The recovery password file must contain exactly one password line");
|
|
73182
|
+
return {
|
|
73183
|
+
password,
|
|
73184
|
+
generated: false
|
|
73185
|
+
};
|
|
73186
|
+
}
|
|
73187
|
+
async function installService(options = {}, overrides = {}) {
|
|
72991
73188
|
const deps = {
|
|
72992
73189
|
...defaultDependencies$2(),
|
|
72993
73190
|
...overrides
|
|
72994
73191
|
};
|
|
72995
|
-
const layout = serviceLayout(deps.platform);
|
|
72996
|
-
|
|
72997
|
-
|
|
72998
|
-
if (
|
|
72999
|
-
|
|
73000
|
-
if (
|
|
73192
|
+
const layout = serviceLayout(deps.platform, options);
|
|
73193
|
+
if (layout.accountMode === "current" && layout.account !== invokingUsername()) throw new Error("Current-account services must run as the invoking OS user");
|
|
73194
|
+
if (layout.accountMode === "dedicated") requireAdministrator(deps.platform);
|
|
73195
|
+
if (options.disableRecoveryLogin && (options.recoveryUsername || options.recoveryPasswordFile)) throw new Error("--no-recovery-login cannot be combined with recovery username/password options");
|
|
73196
|
+
const recoveryUsername = options.disableRecoveryLogin ? null : options.recoveryUsername?.trim() || RECOVERY_USERNAME;
|
|
73197
|
+
if (recoveryUsername !== null && !/^[A-Za-z0-9._@-]{1,128}$/.test(recoveryUsername)) throw new Error("The recovery username contains unsupported characters");
|
|
73198
|
+
const recoveryPassword = options.disableRecoveryLogin ? {
|
|
73199
|
+
password: null,
|
|
73200
|
+
generated: false
|
|
73201
|
+
} : recoveryPasswordFromOptions(options);
|
|
73202
|
+
if (deps.platform === "win32" && recoveryPassword.password !== null && /[%"]/.test(recoveryPassword.password)) throw new Error("Windows recovery passwords cannot contain `%` or `\"`");
|
|
73203
|
+
if (deps.platform === "linux") await installLinux(deps, layout, recoveryUsername, recoveryPassword.password);
|
|
73204
|
+
if (deps.platform === "darwin") await installMac(deps, layout, recoveryUsername, recoveryPassword.password);
|
|
73205
|
+
if (deps.platform === "win32") await installWindows(deps, layout, recoveryUsername, recoveryPassword.password);
|
|
73206
|
+
writeServiceLayout(layout);
|
|
73001
73207
|
return {
|
|
73002
73208
|
layout,
|
|
73003
|
-
recoveryUsername
|
|
73004
|
-
recoveryPassword
|
|
73209
|
+
recoveryUsername,
|
|
73210
|
+
recoveryPassword: recoveryPassword.password,
|
|
73211
|
+
recoveryPasswordGenerated: recoveryPassword.generated
|
|
73005
73212
|
};
|
|
73006
73213
|
}
|
|
73007
73214
|
async function readLog(logPath) {
|
|
@@ -73017,37 +73224,47 @@ async function controlService(action, overrides = {}) {
|
|
|
73017
73224
|
...defaultDependencies$2(),
|
|
73018
73225
|
...overrides
|
|
73019
73226
|
};
|
|
73020
|
-
const layout = serviceLayout(deps.platform);
|
|
73021
|
-
requireAdministrator(deps.platform);
|
|
73227
|
+
const layout = readInstalledServiceLayout(deps.platform) ?? serviceLayout(deps.platform);
|
|
73228
|
+
if (layout.accountMode === "dedicated") requireAdministrator(deps.platform);
|
|
73022
73229
|
if (action === "logs") return await readLog(layout.logPath);
|
|
73023
73230
|
if (deps.platform === "linux") {
|
|
73231
|
+
const systemctl = layout.accountMode === "current" ? ["--user"] : [];
|
|
73024
73232
|
if (action === "uninstall") {
|
|
73025
73233
|
await deps.run("systemctl", [
|
|
73234
|
+
...systemctl,
|
|
73026
73235
|
"disable",
|
|
73027
73236
|
"--now",
|
|
73028
73237
|
layout.serviceId
|
|
73029
73238
|
]).catch(() => {});
|
|
73030
73239
|
fs.rmSync(layout.definitionPath, { force: true });
|
|
73031
|
-
await deps.run("systemctl", ["daemon-reload"]);
|
|
73240
|
+
await deps.run("systemctl", [...systemctl, "daemon-reload"]);
|
|
73241
|
+
fs.rmSync(serviceLayoutMetadataPath(layout), { force: true });
|
|
73032
73242
|
return "Removed the systemd service. ShioriCode data and the service account were preserved.";
|
|
73033
73243
|
}
|
|
73034
73244
|
if (action === "status") return (await deps.run("systemctl", [
|
|
73245
|
+
...systemctl,
|
|
73035
73246
|
"show",
|
|
73036
73247
|
layout.serviceId,
|
|
73037
73248
|
"--no-pager",
|
|
73038
73249
|
"--property=LoadState,UnitFileState,ActiveState,SubState,MainPID,ExecMainStatus"
|
|
73039
73250
|
])).stdout.trim();
|
|
73040
|
-
return (await deps.run("systemctl", [
|
|
73251
|
+
return (await deps.run("systemctl", [
|
|
73252
|
+
...systemctl,
|
|
73253
|
+
action,
|
|
73254
|
+
layout.serviceId
|
|
73255
|
+
])).stdout.trim() || `Service ${action} complete.`;
|
|
73041
73256
|
}
|
|
73042
73257
|
if (deps.platform === "darwin") {
|
|
73043
|
-
const
|
|
73258
|
+
const domain = layout.accountMode === "dedicated" ? "system" : `gui/${invokingUid()}`;
|
|
73259
|
+
const target = `${domain}/${layout.serviceId}`;
|
|
73044
73260
|
if (action === "uninstall") {
|
|
73045
73261
|
await deps.run("launchctl", [
|
|
73046
73262
|
"bootout",
|
|
73047
|
-
|
|
73263
|
+
domain,
|
|
73048
73264
|
layout.definitionPath
|
|
73049
73265
|
]).catch(() => {});
|
|
73050
73266
|
fs.rmSync(layout.definitionPath, { force: true });
|
|
73267
|
+
fs.rmSync(serviceLayoutMetadataPath(layout), { force: true });
|
|
73051
73268
|
return "Removed the launch daemon. ShioriCode data and the service account were preserved.";
|
|
73052
73269
|
}
|
|
73053
73270
|
if (action === "status") try {
|
|
@@ -73058,20 +73275,20 @@ async function controlService(action, overrides = {}) {
|
|
|
73058
73275
|
if (action === "stop") {
|
|
73059
73276
|
await deps.run("launchctl", [
|
|
73060
73277
|
"bootout",
|
|
73061
|
-
|
|
73278
|
+
domain,
|
|
73062
73279
|
layout.definitionPath
|
|
73063
73280
|
]);
|
|
73064
73281
|
return "Service stop complete.";
|
|
73065
73282
|
}
|
|
73066
73283
|
if (action === "restart") await deps.run("launchctl", [
|
|
73067
73284
|
"bootout",
|
|
73068
|
-
|
|
73285
|
+
domain,
|
|
73069
73286
|
layout.definitionPath
|
|
73070
73287
|
]).catch(() => {});
|
|
73071
73288
|
if (action === "start" || action === "restart") {
|
|
73072
73289
|
await deps.run("launchctl", [
|
|
73073
73290
|
"bootstrap",
|
|
73074
|
-
|
|
73291
|
+
domain,
|
|
73075
73292
|
layout.definitionPath
|
|
73076
73293
|
]).catch(() => {});
|
|
73077
73294
|
await deps.run("launchctl", ["enable", target]);
|
|
@@ -73095,6 +73312,7 @@ async function controlService(action, overrides = {}) {
|
|
|
73095
73312
|
layout.serviceId,
|
|
73096
73313
|
"/F"
|
|
73097
73314
|
]);
|
|
73315
|
+
fs.rmSync(serviceLayoutMetadataPath(layout), { force: true });
|
|
73098
73316
|
return "Removed the Windows background task. ShioriCode data and the service account were preserved.";
|
|
73099
73317
|
}
|
|
73100
73318
|
if (action === "status") return (await deps.run("schtasks.exe", [
|
|
@@ -73133,11 +73351,13 @@ function serviceSummary(layout = serviceLayout(supportedPlatform(process.platfor
|
|
|
73133
73351
|
return [
|
|
73134
73352
|
`Platform: ${layout.platform}`,
|
|
73135
73353
|
`Service: ${layout.serviceId}`,
|
|
73136
|
-
`Account: ${layout.account}`,
|
|
73354
|
+
`Account: ${layout.account} (${layout.accountMode})`,
|
|
73355
|
+
`Home: ${layout.homeDir}`,
|
|
73137
73356
|
`Data: ${layout.stateDir}`,
|
|
73138
73357
|
`Workspaces: ${layout.workspaceDir}`,
|
|
73139
73358
|
`Logs: ${layout.logPath}`,
|
|
73140
|
-
`
|
|
73359
|
+
`PATH: ${layout.servicePath}`,
|
|
73360
|
+
`Host: http://127.0.0.1:${layout.port}`
|
|
73141
73361
|
].join("\n");
|
|
73142
73362
|
}
|
|
73143
73363
|
const currentServicePlatform = () => supportedPlatform(os.platform());
|
|
@@ -73146,8 +73366,7 @@ function requireServiceAdministrator(platform = currentServicePlatform()) {
|
|
|
73146
73366
|
return platform;
|
|
73147
73367
|
}
|
|
73148
73368
|
function findInstalledServiceLayout() {
|
|
73149
|
-
|
|
73150
|
-
return fs.existsSync(layout.definitionPath) ? layout : null;
|
|
73369
|
+
return readInstalledServiceLayout(currentServicePlatform());
|
|
73151
73370
|
}
|
|
73152
73371
|
function installedServiceLayout() {
|
|
73153
73372
|
const layout = findInstalledServiceLayout();
|
|
@@ -73158,14 +73377,13 @@ function linkServiceStateDir(layout = installedServiceLayout()) {
|
|
|
73158
73377
|
return path.join(layout.stateDir, "userdata");
|
|
73159
73378
|
}
|
|
73160
73379
|
async function repairServiceStateOwnership(layout = installedServiceLayout()) {
|
|
73161
|
-
if (layout.platform === "win32") return;
|
|
73162
|
-
|
|
73163
|
-
await runDefault("chown", [owner, layout.stateDir]);
|
|
73380
|
+
if (layout.platform === "win32" || layout.accountMode === "current") return;
|
|
73381
|
+
await runDefault("chown", [layout.account, layout.stateDir]);
|
|
73164
73382
|
for (const entry of fs.readdirSync(layout.stateDir, { withFileTypes: true })) {
|
|
73165
73383
|
if (entry.name === "runtime") continue;
|
|
73166
73384
|
await runDefault("chown", [
|
|
73167
73385
|
"-R",
|
|
73168
|
-
|
|
73386
|
+
layout.account,
|
|
73169
73387
|
path.join(layout.stateDir, entry.name)
|
|
73170
73388
|
]);
|
|
73171
73389
|
}
|
|
@@ -73246,9 +73464,8 @@ async function waitForDeviceAuthorization(input) {
|
|
|
73246
73464
|
throw new Error("The GitHub authorization code expired");
|
|
73247
73465
|
}
|
|
73248
73466
|
function serviceLinkContext() {
|
|
73249
|
-
const platform = requireServiceAdministrator();
|
|
73250
73467
|
const layout = installedServiceLayout();
|
|
73251
|
-
if (layout.
|
|
73468
|
+
if (layout.accountMode === "dedicated") requireServiceAdministrator(layout.platform);
|
|
73252
73469
|
const store = new LinkRemoteStore({ stateDir: linkServiceStateDir(layout) });
|
|
73253
73470
|
return {
|
|
73254
73471
|
layout,
|
|
@@ -73414,18 +73631,13 @@ const PROVIDERS = [
|
|
|
73414
73631
|
credentialPaths: [".kimi"]
|
|
73415
73632
|
}
|
|
73416
73633
|
];
|
|
73417
|
-
function
|
|
73418
|
-
if (platform === "darwin") return "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
73419
|
-
if (platform === "linux") return "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
73420
|
-
return process.env.Path ?? process.env.PATH ?? "";
|
|
73421
|
-
}
|
|
73422
|
-
async function binaryVersion(binary, home) {
|
|
73634
|
+
async function binaryVersion(binary, home, configuredPath) {
|
|
73423
73635
|
try {
|
|
73424
73636
|
const result = await execFile$4(binary, ["--version"], {
|
|
73425
73637
|
env: {
|
|
73426
73638
|
...process.env,
|
|
73427
73639
|
HOME: home,
|
|
73428
|
-
PATH:
|
|
73640
|
+
PATH: configuredPath
|
|
73429
73641
|
},
|
|
73430
73642
|
timeout: 5e3,
|
|
73431
73643
|
windowsHide: true
|
|
@@ -73439,26 +73651,27 @@ function hasCredentials(home, candidates) {
|
|
|
73439
73651
|
return candidates.some((candidate) => fs.existsSync(path.join(home, candidate)));
|
|
73440
73652
|
}
|
|
73441
73653
|
function serviceAccountCommand(command) {
|
|
73442
|
-
const layout = serviceLayout(currentServicePlatform());
|
|
73654
|
+
const layout = findInstalledServiceLayout() ?? serviceLayout(currentServicePlatform());
|
|
73655
|
+
if (layout.accountMode === "current") return command;
|
|
73443
73656
|
if (layout.platform === "linux") return `sudo -u ${layout.account} -H ${command}`;
|
|
73444
73657
|
if (layout.platform === "darwin") return `sudo -u ${layout.account} -H env PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin ${command}`;
|
|
73445
73658
|
return `${command} (run it for the ${layout.account} service account)`;
|
|
73446
73659
|
}
|
|
73447
73660
|
async function providerDoctor() {
|
|
73448
|
-
const layout = serviceLayout(currentServicePlatform());
|
|
73661
|
+
const layout = findInstalledServiceLayout() ?? serviceLayout(currentServicePlatform());
|
|
73449
73662
|
const lines = [
|
|
73450
73663
|
`Service account: ${layout.account}`,
|
|
73451
|
-
`Service home: ${layout.
|
|
73664
|
+
`Service home: ${layout.homeDir}`,
|
|
73452
73665
|
""
|
|
73453
73666
|
];
|
|
73454
73667
|
for (const provider of PROVIDERS) {
|
|
73455
|
-
const version = await binaryVersion(provider.binary, layout.
|
|
73668
|
+
const version = await binaryVersion(provider.binary, layout.homeDir, layout.servicePath);
|
|
73456
73669
|
if (!version) {
|
|
73457
73670
|
lines.push(`✗ ${provider.name}: not found in the service PATH`);
|
|
73458
73671
|
lines.push(` Install: ${provider.installHint}`);
|
|
73459
73672
|
continue;
|
|
73460
73673
|
}
|
|
73461
|
-
const authenticated = hasCredentials(layout.
|
|
73674
|
+
const authenticated = hasCredentials(layout.homeDir, provider.credentialPaths);
|
|
73462
73675
|
lines.push(`✓ ${provider.name}: ${version}`);
|
|
73463
73676
|
lines.push(authenticated ? " Credentials detected in the service home" : ` Sign in: ${serviceAccountCommand(provider.authHint)}`);
|
|
73464
73677
|
}
|
|
@@ -83765,7 +83978,7 @@ function resolveTarget(baseDir, deps) {
|
|
|
83765
83978
|
}
|
|
83766
83979
|
async function withRemoteRpc(target, deps, run) {
|
|
83767
83980
|
if (!target.service) return await deps.withLocalRpc(target.baseDir, run);
|
|
83768
|
-
deps.requireAdministrator(target.service.platform);
|
|
83981
|
+
if (target.service.accountMode === "dedicated") deps.requireAdministrator(target.service.platform);
|
|
83769
83982
|
const connection = await deps.connectService(target.service.stateDir);
|
|
83770
83983
|
if (!connection) throw new Error("The ShioriCode service is installed but not reachable. Run `shioricode service start`, then retry.");
|
|
83771
83984
|
try {
|
|
@@ -216800,18 +217013,21 @@ function builtInShioriMcpServers(input) {
|
|
|
216800
217013
|
return servers.map((server) => Object.assign({}, server, { providers: [provider] }));
|
|
216801
217014
|
}
|
|
216802
217015
|
function makeBuiltInStdioMcpServerEntry(name, subcommand, env) {
|
|
216803
|
-
|
|
217016
|
+
return {
|
|
216804
217017
|
name,
|
|
216805
217018
|
transport: "stdio",
|
|
216806
217019
|
command: process.execPath,
|
|
216807
217020
|
args: [...serverEntrypointArgs(), subcommand],
|
|
216808
217021
|
enabled: true,
|
|
216809
|
-
providers: []
|
|
217022
|
+
providers: [],
|
|
217023
|
+
env: makeBuiltInStdioMcpEnvironment(env)
|
|
217024
|
+
};
|
|
217025
|
+
}
|
|
217026
|
+
function makeBuiltInStdioMcpEnvironment(env) {
|
|
217027
|
+
return {
|
|
217028
|
+
...env,
|
|
217029
|
+
ELECTRON_RUN_AS_NODE: "1"
|
|
216810
217030
|
};
|
|
216811
|
-
return env ? {
|
|
216812
|
-
...entry,
|
|
216813
|
-
env
|
|
216814
|
-
} : entry;
|
|
216815
217031
|
}
|
|
216816
217032
|
function threadGoalMcpServerEntry(input) {
|
|
216817
217033
|
return makeBuiltInStdioMcpServerEntry(THREAD_GOAL_MCP_SERVER_NAME, "thread-goal-mcp", {
|
|
@@ -219725,7 +219941,7 @@ const getTelemetryIdentifier = gen$5(function* () {
|
|
|
219725
219941
|
|
|
219726
219942
|
//#endregion
|
|
219727
219943
|
//#region package.json
|
|
219728
|
-
var version$1 = "0.5.
|
|
219944
|
+
var version$1 = "0.5.3";
|
|
219729
219945
|
|
|
219730
219946
|
//#endregion
|
|
219731
219947
|
//#region src/telemetry/Layers/AnalyticsService.ts
|
|
@@ -703211,14 +703427,42 @@ const serveCommand = make$166("serve", commandFlags).pipe(withDescription$3("Run
|
|
|
703211
703427
|
const serviceActionCommand = (action, description) => make$166(action).pipe(withDescription$3(description), withHandler(() => promise$5(async () => {
|
|
703212
703428
|
console.log(await controlService(action));
|
|
703213
703429
|
})));
|
|
703430
|
+
const serviceInstallFlags = {
|
|
703431
|
+
accountMode: choice$2("account", ["dedicated", "current"]).pipe(withDescription$4("OS account strategy: create/reuse a dedicated service user, or run as the invoking user."), withDefault$2("dedicated")),
|
|
703432
|
+
account: string$10("user").pipe(withDescription$4("Override the OS username. Dedicated mode creates it when missing; current mode requires it to exist."), optional$3),
|
|
703433
|
+
homeDir: string$10("home-dir").pipe(withDescription$4("HOME exposed to provider CLIs in the service process."), optional$3),
|
|
703434
|
+
stateDir: string$10("state-dir").pipe(withDescription$4("ShioriCode service data directory."), optional$3),
|
|
703435
|
+
workspaceDir: string$10("workspace-dir").pipe(withDescription$4("Default working directory for the service."), optional$3),
|
|
703436
|
+
logPath: string$10("log-file").pipe(withDescription$4("Log file for stdout and stderr from the background service."), optional$3),
|
|
703437
|
+
servicePath: string$10("service-path").pipe(withDescription$4("PATH used to discover provider CLIs such as codex, claude, and kimi in the service."), optional$3),
|
|
703438
|
+
port: integer$4("port").pipe(withSchema(PortSchema), withDescription$4("Loopback port for the background service."), optional$3),
|
|
703439
|
+
recoveryUsername: string$10("recovery-username").pipe(withDescription$4("Username for direct recovery login. Defaults to `recovery`."), optional$3),
|
|
703440
|
+
recoveryPasswordFile: string$10("recovery-password-file").pipe(withDescription$4("Read the direct recovery password from a local file instead of generating one. The file must not be empty."), optional$3),
|
|
703441
|
+
disableRecoveryLogin: boolean$8("no-recovery-login").pipe(withDescription$4("Disable direct username/password recovery login. ShioriCode Link remains GitHub-only."), withDefault$2(false))
|
|
703442
|
+
};
|
|
703214
703443
|
const serviceCommand = make$166("service").pipe(withDescription$3("Install and control the OS background service."), withSubcommands([
|
|
703215
|
-
make$166("install").pipe(withDescription$3("Install and start ShioriCode
|
|
703216
|
-
const result = await installService(
|
|
703444
|
+
make$166("install", serviceInstallFlags).pipe(withDescription$3("Install and start ShioriCode as a dedicated or existing OS user."), withHandler((flags) => promise$5(async () => {
|
|
703445
|
+
const result = await installService({
|
|
703446
|
+
accountMode: flags.accountMode,
|
|
703447
|
+
account: getOrUndefined$3(flags.account),
|
|
703448
|
+
homeDir: getOrUndefined$3(flags.homeDir),
|
|
703449
|
+
stateDir: getOrUndefined$3(flags.stateDir),
|
|
703450
|
+
workspaceDir: getOrUndefined$3(flags.workspaceDir),
|
|
703451
|
+
logPath: getOrUndefined$3(flags.logPath),
|
|
703452
|
+
servicePath: getOrUndefined$3(flags.servicePath),
|
|
703453
|
+
port: getOrUndefined$3(flags.port),
|
|
703454
|
+
recoveryUsername: getOrUndefined$3(flags.recoveryUsername),
|
|
703455
|
+
recoveryPasswordFile: getOrUndefined$3(flags.recoveryPasswordFile),
|
|
703456
|
+
disableRecoveryLogin: flags.disableRecoveryLogin
|
|
703457
|
+
});
|
|
703217
703458
|
console.log("ShioriCode service installed and started.\n");
|
|
703218
703459
|
console.log(serviceSummary(result.layout));
|
|
703219
|
-
console.log("\nLocal recovery
|
|
703220
|
-
console.log(
|
|
703221
|
-
|
|
703460
|
+
console.log("\nLocal recovery login:");
|
|
703461
|
+
if (result.recoveryUsername === null || result.recoveryPassword === null) console.log("Disabled. Hosted access uses GitHub through ShioriCode Link.");
|
|
703462
|
+
else {
|
|
703463
|
+
console.log(`Username: ${result.recoveryUsername}`);
|
|
703464
|
+
console.log(result.recoveryPasswordGenerated ? `Generated password: ${result.recoveryPassword}\nStore it securely; it is shown once.` : "Password: loaded from --recovery-password-file (not echoed)");
|
|
703465
|
+
}
|
|
703222
703466
|
console.log("\nNext: shioricode link connect");
|
|
703223
703467
|
}))),
|
|
703224
703468
|
serviceActionCommand("start", "Start the ShioriCode service."),
|