harness-wingman 0.9.7 → 0.9.8
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/apps/cli/dist/main.js
CHANGED
|
@@ -283,6 +283,14 @@ var init_cli = __esm({
|
|
|
283
283
|
"cli.licenseJson": "The license file is not valid JSON",
|
|
284
284
|
"cli.licenseFields": "The license file is missing token/signature fields",
|
|
285
285
|
"cli.licenseSignature": "License signature verification failed (modified content or issued for another product)",
|
|
286
|
+
"cli.licenseOtherDevice": "This license is bound to another device",
|
|
287
|
+
"cli.licenseDeviceUnavailable": "This Mac\u2019s device identifier is unavailable. Activation is unavailable; existing unbound licenses keep their current validity.",
|
|
288
|
+
"cli.licenseDeviceLimit": "This license is already activated on 3 devices",
|
|
289
|
+
"cli.licenseActivationExpired": "This license has expired. Renew it before activating this device.",
|
|
290
|
+
"cli.licenseUnbindRequiresActiveDevice": "Unbind devices from an activated device with an unexpired license.",
|
|
291
|
+
"cli.licenseDeviceRevoked": "This device\u2019s activation was revoked. Click \u201CActivate this device\u201D to activate it again.",
|
|
292
|
+
"cli.licenseFixturesOffline": "Fixture mode does not contact the online license server",
|
|
293
|
+
"cli.licenseDevicesInvalid": "The license server returned an invalid device list",
|
|
286
294
|
"cli.licenseClaimsFields": "License payload fields are incomplete",
|
|
287
295
|
"cli.licenseClaimsDecode": "The license payload could not be decoded",
|
|
288
296
|
"cli.licenseValidUntil": "{plan} valid until {exp}",
|
|
@@ -443,6 +451,10 @@ var init_panel = __esm({
|
|
|
443
451
|
"panel.mobile.disclosure": "After pairing, your Mac uploads encrypted project and session status for the phone app to read. The relay only sees ciphertext.",
|
|
444
452
|
"panel.mobile.scan": "Scan with the Wingman app on your phone",
|
|
445
453
|
"panel.mobile.qrLabel": "Phone pairing QR code",
|
|
454
|
+
"panel.mobile.qrExpired": "QR code expired",
|
|
455
|
+
"panel.mobile.expired": "Expired",
|
|
456
|
+
"panel.mobile.regenerate": "Regenerate",
|
|
457
|
+
"panel.mobile.expiredTip": "The pairing window has ended. Generate a new QR code to scan.",
|
|
446
458
|
"panel.mobile.autoClose": "Closes automatically after you scan on your phone",
|
|
447
459
|
"panel.mobile.cancel": "Cancel pairing",
|
|
448
460
|
"panel.mobile.unnamed": "Phone",
|
|
@@ -559,6 +571,23 @@ var init_panel = __esm({
|
|
|
559
571
|
"panel.license.licenseImportedNewStatusUnknownRefresh": "License imported (new status unknown; refresh this page)",
|
|
560
572
|
"panel.license.dropOneLicenseJsonFileAt": "Drop one license.json file at a time (received {p0})",
|
|
561
573
|
"panel.license.onlyJsonFilesAreAcceptedReceived": "Only .json files are accepted (received {p0})",
|
|
574
|
+
"panel.license.deviceActive": "This device is activated \xB7 {count}/{max} devices",
|
|
575
|
+
"panel.license.deviceLimit": "This license is activated on {count} devices",
|
|
576
|
+
"panel.license.deviceNotRegistered": "This device is not activated \xB7 {count}/{max} devices",
|
|
577
|
+
"panel.license.deviceNotRegisteredTip": "Import the license again to activate this device when a slot is available",
|
|
578
|
+
"panel.license.deviceRevoked": "Activation for this device was revoked",
|
|
579
|
+
"panel.license.deviceRevokedTip": "Your existing license is kept until it expires. Automatic and manual refresh cannot reactivate this device. Select Activate this device when a device slot is available.",
|
|
580
|
+
"panel.license.activateDevice": "Activate this device",
|
|
581
|
+
"panel.license.deviceUnavailable": "Cannot read this device\u2019s identifier; activation is unavailable",
|
|
582
|
+
"panel.license.devicePending": "This device is not activated yet",
|
|
583
|
+
"panel.license.deviceTip": "Each subscription supports up to {max} devices. Licenses renew automatically every 24 hours. View devices.",
|
|
584
|
+
"panel.license.deviceLimitTip": "Unbind an old device from an activated device, then import the license again here. Any existing local license is kept until it expires.",
|
|
585
|
+
"panel.license.deviceManageElsewhere": "Unbind from an activated device with an unexpired license",
|
|
586
|
+
"panel.license.devices": "Activated devices",
|
|
587
|
+
"panel.license.unnamedDevice": "Unnamed device",
|
|
588
|
+
"panel.license.deviceLastSeen": "Last renewed: {at}",
|
|
589
|
+
"panel.license.unbind": "Unbind",
|
|
590
|
+
"panel.license.unbindTip": "Unbinding revokes renewals for that device. Its existing license is kept until it expires. Only importing a license again or selecting Activate this device on that machine can reactivate it.",
|
|
562
591
|
"panel.live.sessionStarted": "Session started",
|
|
563
592
|
"panel.live.sessionEnded": "Session ended",
|
|
564
593
|
"panel.live.promptSubmitted": "Prompt submitted",
|
|
@@ -1639,6 +1668,14 @@ var init_cli2 = __esm({
|
|
|
1639
1668
|
"cli.licenseJson": "license \u6587\u4EF6\u4E0D\u662F\u5408\u6CD5 JSON",
|
|
1640
1669
|
"cli.licenseFields": "license \u6587\u4EF6\u7F3A token/signature \u5B57\u6BB5",
|
|
1641
1670
|
"cli.licenseSignature": "license \u9A8C\u7B7E\u5931\u8D25(\u5185\u5BB9\u88AB\u6539\u52A8\u6216\u975E\u672C\u4EA7\u54C1\u7B7E\u53D1)",
|
|
1671
|
+
"cli.licenseOtherDevice": "\u6B64 license \u7ED1\u5B9A\u4E86\u5176\u4ED6\u8BBE\u5907",
|
|
1672
|
+
"cli.licenseDeviceUnavailable": "\u65E0\u6CD5\u83B7\u53D6\u672C\u673A\u8BBE\u5907\u6807\u8BC6\uFF0C\u6682\u65F6\u65E0\u6CD5\u6FC0\u6D3B\uFF1B\u5DF2\u6709\u672A\u7ED1\u5B9A license \u4ECD\u6309\u539F\u6709\u6548\u671F\u4F7F\u7528\u3002",
|
|
1673
|
+
"cli.licenseDeviceLimit": "\u6B64 license \u5DF2\u5728 3 \u53F0\u8BBE\u5907\u6FC0\u6D3B",
|
|
1674
|
+
"cli.licenseActivationExpired": "\u6B64 license \u5DF2\u5230\u671F\uFF0C\u8BF7\u5148\u7EED\u671F\u540E\u518D\u6FC0\u6D3B\u672C\u673A\u3002",
|
|
1675
|
+
"cli.licenseUnbindRequiresActiveDevice": "\u8BF7\u4ECE\u5DF2\u6FC0\u6D3B\u4E14\u6388\u6743\u672A\u5230\u671F\u7684\u8BBE\u5907\u89E3\u7ED1\u3002",
|
|
1676
|
+
"cli.licenseDeviceRevoked": "\u672C\u673A\u6FC0\u6D3B\u5DF2\u88AB\u64A4\u9500\uFF1B\u5982\u9700\u7EE7\u7EED\u4F7F\u7528\uFF0C\u8BF7\u70B9\u51FB\u300C\u6FC0\u6D3B\u672C\u673A\u300D\u3002",
|
|
1677
|
+
"cli.licenseFixturesOffline": "\u6837\u4F8B\u6A21\u5F0F\u4E0D\u4F1A\u8054\u7CFB\u5728\u7EBF\u7B7E\u53D1\u7AEF",
|
|
1678
|
+
"cli.licenseDevicesInvalid": "\u7B7E\u53D1\u7AEF\u8FD4\u56DE\u7684\u8BBE\u5907\u5217\u8868\u65E0\u6548",
|
|
1642
1679
|
"cli.licenseClaimsFields": "license \u8F7D\u8377\u5B57\u6BB5\u4E0D\u5B8C\u6574",
|
|
1643
1680
|
"cli.licenseClaimsDecode": "license \u8F7D\u8377\u65E0\u6CD5\u89E3\u7801",
|
|
1644
1681
|
"cli.licenseValidUntil": "{plan}\u6709\u6548\u81F3 {exp}",
|
|
@@ -1799,6 +1836,10 @@ var init_panel2 = __esm({
|
|
|
1799
1836
|
"panel.mobile.disclosure": "\u914D\u5BF9\u540E Mac \u4F1A\u628A\u9879\u76EE\u4E0E\u4F1A\u8BDD\u72B6\u6001\u52A0\u5BC6\u4E0A\u4F20,\u624B\u673A App \u624D\u80FD\u770B\u5230;\u4E2D\u8F6C\u53EA\u89C1\u5BC6\u6587",
|
|
1800
1837
|
"panel.mobile.scan": "\u6253\u5F00\u624B\u673A\u4E0A\u7684 Wingman App \u626B\u7801\u914D\u5BF9",
|
|
1801
1838
|
"panel.mobile.qrLabel": "\u624B\u673A\u914D\u5BF9\u4E8C\u7EF4\u7801",
|
|
1839
|
+
"panel.mobile.qrExpired": "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F",
|
|
1840
|
+
"panel.mobile.expired": "\u5DF2\u8FC7\u671F",
|
|
1841
|
+
"panel.mobile.regenerate": "\u91CD\u65B0\u751F\u6210",
|
|
1842
|
+
"panel.mobile.expiredTip": "\u914D\u5BF9\u7B49\u5F85\u7A97\u53E3\u5DF2\u7ED3\u675F\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801\u540E\u626B\u7801\u3002",
|
|
1802
1843
|
"panel.mobile.autoClose": "\u5DF2\u5728\u624B\u673A\u4E0A\u626B\u7801\u540E\u81EA\u52A8\u5173\u95ED",
|
|
1803
1844
|
"panel.mobile.cancel": "\u53D6\u6D88\u914D\u5BF9",
|
|
1804
1845
|
"panel.mobile.unnamed": "\u624B\u673A",
|
|
@@ -1915,6 +1956,23 @@ var init_panel2 = __esm({
|
|
|
1915
1956
|
"panel.license.licenseImportedNewStatusUnknownRefresh": "license \u5DF2\u5BFC\u5165(\u65B0\u72B6\u6001\u672A\u77E5,\u5237\u65B0\u672C\u9875\u67E5\u770B)",
|
|
1916
1957
|
"panel.license.dropOneLicenseJsonFileAt": "\u4E00\u6B21\u62D6\u5165 1 \u4E2A license.json \u6587\u4EF6(\u6536\u5230 {p0} \u4E2A)",
|
|
1917
1958
|
"panel.license.onlyJsonFilesAreAcceptedReceived": "\u53EA\u8BA4 .json \u6587\u4EF6(\u6536\u5230 {p0})",
|
|
1959
|
+
"panel.license.deviceActive": "\u672C\u673A\u5DF2\u6FC0\u6D3B \xB7 {count}/{max} \u53F0",
|
|
1960
|
+
"panel.license.deviceLimit": "\u6B64 license \u5DF2\u5728 {count} \u53F0\u8BBE\u5907\u6FC0\u6D3B",
|
|
1961
|
+
"panel.license.deviceNotRegistered": "\u672C\u673A\u5C1A\u672A\u6FC0\u6D3B \xB7 {count}/{max} \u53F0",
|
|
1962
|
+
"panel.license.deviceNotRegisteredTip": "\u6709\u8BBE\u5907\u540D\u989D\u65F6\uFF0C\u8BF7\u91CD\u65B0\u5BFC\u5165 license \u4EE5\u6FC0\u6D3B\u672C\u673A",
|
|
1963
|
+
"panel.license.deviceRevoked": "\u672C\u673A\u6FC0\u6D3B\u5DF2\u88AB\u64A4\u9500",
|
|
1964
|
+
"panel.license.deviceRevokedTip": "\u73B0\u6709\u6388\u6743\u4FDD\u7559\u81F3\u5230\u671F\uFF0C\u81EA\u52A8\u6216\u624B\u52A8\u5237\u65B0\u4E0D\u4F1A\u91CD\u65B0\u6FC0\u6D3B\u3002\u82E5\u8981\u7EE7\u7EED\u5728\u672C\u673A\u4F7F\u7528\uFF0C\u8BF7\u5728\u6709\u8BBE\u5907\u540D\u989D\u65F6\u70B9\u300C\u6FC0\u6D3B\u672C\u673A\u300D\u3002",
|
|
1965
|
+
"panel.license.activateDevice": "\u6FC0\u6D3B\u672C\u673A",
|
|
1966
|
+
"panel.license.deviceUnavailable": "\u65E0\u6CD5\u8BFB\u53D6\u672C\u673A\u8BBE\u5907\u6807\u8BC6\uFF0C\u6682\u65F6\u65E0\u6CD5\u6FC0\u6D3B",
|
|
1967
|
+
"panel.license.devicePending": "\u672C\u673A\u5C1A\u672A\u6FC0\u6D3B",
|
|
1968
|
+
"panel.license.deviceTip": "\u6BCF\u6761\u8BA2\u9605\u6700\u591A {max} \u53F0\u8BBE\u5907\uFF0C\u6388\u6743\u6BCF 24 \u5C0F\u65F6\u81EA\u52A8\u7EED\u671F\u3002\u70B9\u6B64\u67E5\u770B\u8BBE\u5907\u3002",
|
|
1969
|
+
"panel.license.deviceLimitTip": "\u8BF7\u5148\u5728\u5DF2\u6FC0\u6D3B\u8BBE\u5907\u4E0A\u89E3\u7ED1\u4E00\u53F0\u65E7\u8BBE\u5907\uFF0C\u518D\u5728\u672C\u673A\u91CD\u65B0\u5BFC\u5165 license\uFF1B\u672C\u673A\u5DF2\u6709\u6388\u6743\u4FDD\u7559\u81F3\u5176\u5230\u671F\u3002",
|
|
1970
|
+
"panel.license.deviceManageElsewhere": "\u8BF7\u5728\u5DF2\u6FC0\u6D3B\u4E14\u6388\u6743\u672A\u5230\u671F\u7684\u8BBE\u5907\u4E0A\u89E3\u7ED1",
|
|
1971
|
+
"panel.license.devices": "\u5DF2\u6FC0\u6D3B\u8BBE\u5907",
|
|
1972
|
+
"panel.license.unnamedDevice": "\u672A\u547D\u540D\u8BBE\u5907",
|
|
1973
|
+
"panel.license.deviceLastSeen": "\u6700\u8FD1\u7EED\u671F\uFF1A{at}",
|
|
1974
|
+
"panel.license.unbind": "\u89E3\u7ED1",
|
|
1975
|
+
"panel.license.unbindTip": "\u89E3\u7ED1\u4F1A\u64A4\u9500\u8BE5\u8BBE\u5907\u7684\u7EED\u671F\u8D44\u683C\uFF0C\u73B0\u6709\u6388\u6743\u4FDD\u7559\u81F3\u5230\u671F\uFF1B\u53EA\u6709\u5728\u90A3\u53F0\u673A\u5668\u91CD\u65B0\u5BFC\u5165 license \u6216\u70B9\u300C\u6FC0\u6D3B\u672C\u673A\u300D\u624D\u4F1A\u91CD\u65B0\u6FC0\u6D3B\u3002",
|
|
1918
1976
|
"panel.live.sessionStarted": "\u4F1A\u8BDD\u5F00\u59CB",
|
|
1919
1977
|
"panel.live.sessionEnded": "\u4F1A\u8BDD\u7ED3\u675F",
|
|
1920
1978
|
"panel.live.promptSubmitted": "\u63D0\u4EA4\u63D0\u793A",
|
|
@@ -12582,7 +12640,7 @@ var init_json_edit = __esm({
|
|
|
12582
12640
|
});
|
|
12583
12641
|
|
|
12584
12642
|
// packages/adapter-cc/dist/toggle.js
|
|
12585
|
-
import { createHash as
|
|
12643
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
12586
12644
|
import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync7 } from "node:fs";
|
|
12587
12645
|
import { homedir as homedir6 } from "node:os";
|
|
12588
12646
|
import { basename as basename4, join as join12, resolve as resolve3 } from "node:path";
|
|
@@ -12692,7 +12750,7 @@ function isCcFeatureId(id2) {
|
|
|
12692
12750
|
return FEATURE_KEYS.some((key) => id2 === `cc:gates:user:${key}`);
|
|
12693
12751
|
}
|
|
12694
12752
|
function featureBytesHash(raw) {
|
|
12695
|
-
return raw === void 0 ? null :
|
|
12753
|
+
return raw === void 0 ? null : createHash5("sha256").update(raw).digest("hex");
|
|
12696
12754
|
}
|
|
12697
12755
|
function matchesTarget(data, key, target) {
|
|
12698
12756
|
if (featureValue(data, key) !== target)
|
|
@@ -17489,6 +17547,38 @@ import { verify } from "node:crypto";
|
|
|
17489
17547
|
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
17490
17548
|
import { homedir as homedir3 } from "node:os";
|
|
17491
17549
|
import { join as join7 } from "node:path";
|
|
17550
|
+
|
|
17551
|
+
// apps/cli/dist/license-device.js
|
|
17552
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
17553
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
17554
|
+
import { hostname } from "node:os";
|
|
17555
|
+
var cachedDevice;
|
|
17556
|
+
function getLicenseDevice() {
|
|
17557
|
+
const device = cachedDevice ?? readLicenseDevice();
|
|
17558
|
+
if (device)
|
|
17559
|
+
cachedDevice = device;
|
|
17560
|
+
return device;
|
|
17561
|
+
}
|
|
17562
|
+
function readLicenseDevice(options = {}) {
|
|
17563
|
+
if ((options.platform ?? process.platform) !== "darwin")
|
|
17564
|
+
return null;
|
|
17565
|
+
try {
|
|
17566
|
+
const output = (options.read ?? (() => execFileSync2("/usr/sbin/ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
|
|
17567
|
+
encoding: "utf8",
|
|
17568
|
+
timeout: 2e3
|
|
17569
|
+
})))();
|
|
17570
|
+
const uuid = /"IOPlatformUUID"\s*=\s*"([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})"/i.exec(output)?.[1];
|
|
17571
|
+
if (!uuid || /^0{8}-0{4}-0{4}-0{4}-0{12}$/.test(uuid))
|
|
17572
|
+
return null;
|
|
17573
|
+
const id2 = createHash3("sha256").update(uuid.toUpperCase()).digest("base64url").slice(0, 22);
|
|
17574
|
+
const name = (options.hostname ?? hostname()).split(".")[0];
|
|
17575
|
+
return { id: id2, ...name ? { name } : {} };
|
|
17576
|
+
} catch {
|
|
17577
|
+
return null;
|
|
17578
|
+
}
|
|
17579
|
+
}
|
|
17580
|
+
|
|
17581
|
+
// apps/cli/dist/license.js
|
|
17492
17582
|
var OFFLINE_GRACE_DAYS = 14;
|
|
17493
17583
|
var PROD_PUBLIC_KEY_PEM = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAyU4yO0SJcflt/CV77wqVxIwCkM52CYGC6qrPGP8dMME=\n-----END PUBLIC KEY-----\n";
|
|
17494
17584
|
function checkLicense(options = {}) {
|
|
@@ -17510,7 +17600,15 @@ function licenseStatus(options = {}) {
|
|
|
17510
17600
|
if (publicKeyPem === "") {
|
|
17511
17601
|
return { state: "active", detail: t("cli.licenseDevelopment") };
|
|
17512
17602
|
}
|
|
17513
|
-
|
|
17603
|
+
const status3 = verifyEntitlement(raw, options.now ?? /* @__PURE__ */ new Date(), publicKeyPem);
|
|
17604
|
+
if (status3.claims?.dev !== void 0) {
|
|
17605
|
+
const device = options.device === void 0 ? getLicenseDevice() : options.device;
|
|
17606
|
+
if (!device)
|
|
17607
|
+
return { state: "invalid", detail: t("cli.licenseDeviceUnavailable") };
|
|
17608
|
+
if (status3.claims.dev !== device.id)
|
|
17609
|
+
return { state: "invalid", detail: t("cli.licenseOtherDevice") };
|
|
17610
|
+
}
|
|
17611
|
+
return status3;
|
|
17514
17612
|
}
|
|
17515
17613
|
function verifyEntitlement(raw, now, publicKeyPem) {
|
|
17516
17614
|
let parsed;
|
|
@@ -17533,10 +17631,16 @@ function verifyEntitlement(raw, now, publicKeyPem) {
|
|
|
17533
17631
|
let claims;
|
|
17534
17632
|
try {
|
|
17535
17633
|
const decoded = JSON.parse(Buffer.from(parsed.token, "base64url").toString("utf8"));
|
|
17536
|
-
if (typeof decoded.sub !== "string" || decoded.plan !== "trial" && decoded.plan !== "subscription" || typeof decoded.iat !== "string" || typeof decoded.exp !== "string" || Number.isNaN(Date.parse(decoded.exp))) {
|
|
17634
|
+
if (typeof decoded.sub !== "string" || decoded.plan !== "trial" && decoded.plan !== "subscription" || typeof decoded.iat !== "string" || typeof decoded.exp !== "string" || Number.isNaN(Date.parse(decoded.exp)) || decoded.dev !== void 0 && (typeof decoded.dev !== "string" || !decoded.dev)) {
|
|
17537
17635
|
return { state: "invalid", detail: t("cli.licenseClaimsFields") };
|
|
17538
17636
|
}
|
|
17539
|
-
claims = {
|
|
17637
|
+
claims = {
|
|
17638
|
+
sub: decoded.sub,
|
|
17639
|
+
plan: decoded.plan,
|
|
17640
|
+
iat: decoded.iat,
|
|
17641
|
+
exp: decoded.exp,
|
|
17642
|
+
...typeof decoded.dev === "string" ? { dev: decoded.dev } : {}
|
|
17643
|
+
};
|
|
17540
17644
|
} catch {
|
|
17541
17645
|
return { state: "invalid", detail: t("cli.licenseClaimsDecode") };
|
|
17542
17646
|
}
|
|
@@ -17616,7 +17720,34 @@ import { copyFileSync, readFileSync as readFileSync4, renameSync as renameSync2,
|
|
|
17616
17720
|
import { homedir as homedir4 } from "node:os";
|
|
17617
17721
|
import { dirname as dirname6, join as join8 } from "node:path";
|
|
17618
17722
|
var DEFAULT_LICENSE_SERVER = "https://api.getwingman.dev";
|
|
17723
|
+
function parseDevices(value) {
|
|
17724
|
+
if (!Array.isArray(value) || value.length > 3)
|
|
17725
|
+
return void 0;
|
|
17726
|
+
if (!value.every((item) => typeof item === "object" && item !== null && "id" in item && typeof item.id === "string" && "registeredAt" in item && typeof item.registeredAt === "string" && !Number.isNaN(Date.parse(item.registeredAt)) && "renewedAt" in item && typeof item.renewedAt === "string" && !Number.isNaN(Date.parse(item.renewedAt)) && (!("name" in item) || typeof item.name === "string")))
|
|
17727
|
+
return void 0;
|
|
17728
|
+
return value.map((item) => ({
|
|
17729
|
+
id: item.id,
|
|
17730
|
+
registeredAt: item.registeredAt,
|
|
17731
|
+
renewedAt: item.renewedAt,
|
|
17732
|
+
...item.name ? { name: item.name } : {}
|
|
17733
|
+
}));
|
|
17734
|
+
}
|
|
17735
|
+
function licenseNetworkAllowed(options, baseUrl) {
|
|
17736
|
+
if (!options.offline || options.fetchFn)
|
|
17737
|
+
return true;
|
|
17738
|
+
try {
|
|
17739
|
+
return ["127.0.0.1", "localhost", "[::1]"].includes(new URL(baseUrl).hostname);
|
|
17740
|
+
} catch {
|
|
17741
|
+
return false;
|
|
17742
|
+
}
|
|
17743
|
+
}
|
|
17619
17744
|
async function refreshLicense(options = {}) {
|
|
17745
|
+
return renewLicense(options, "refresh");
|
|
17746
|
+
}
|
|
17747
|
+
async function activateLicense(options) {
|
|
17748
|
+
return renewLicense(options, "activate");
|
|
17749
|
+
}
|
|
17750
|
+
async function renewLicense(options, action) {
|
|
17620
17751
|
const home = options.homeDir ?? homedir4();
|
|
17621
17752
|
const baseUrl = options.baseUrl ?? process.env.WINGMAN_LICENSE_SERVER ?? DEFAULT_LICENSE_SERVER;
|
|
17622
17753
|
const licensePath = join8(home, ".wingman", "license.json");
|
|
@@ -17630,12 +17761,33 @@ async function refreshLicense(options = {}) {
|
|
|
17630
17761
|
reason: "missing"
|
|
17631
17762
|
};
|
|
17632
17763
|
}
|
|
17764
|
+
if (!licenseNetworkAllowed(options, baseUrl)) {
|
|
17765
|
+
return { ok: false, message: t("cli.licenseFixturesOffline"), reason: "offline" };
|
|
17766
|
+
}
|
|
17767
|
+
const publicKeyPem = options.publicKeyPem ?? PROD_PUBLIC_KEY_PEM;
|
|
17768
|
+
if (publicKeyPem !== "") {
|
|
17769
|
+
const status3 = verifyEntitlement(raw, options.now ?? /* @__PURE__ */ new Date(), publicKeyPem);
|
|
17770
|
+
if (status3.state === "invalid")
|
|
17771
|
+
return { ok: false, message: t("cli.licenseSignature"), reason: "invalid-license" };
|
|
17772
|
+
if (action === "activate" && status3.state !== "active")
|
|
17773
|
+
return { ok: false, message: t("cli.licenseActivationExpired"), reason: "invalid-license" };
|
|
17774
|
+
}
|
|
17775
|
+
const device = options.device === void 0 ? getLicenseDevice() : options.device;
|
|
17776
|
+
if (!device)
|
|
17777
|
+
return { ok: false, message: t("cli.licenseDeviceUnavailable"), reason: "device-unavailable" };
|
|
17778
|
+
let requestBody;
|
|
17779
|
+
try {
|
|
17780
|
+
requestBody = JSON.stringify(action === "activate" ? { license: JSON.parse(raw), action, ...device } : { ...JSON.parse(raw), device: { id: device.id } });
|
|
17781
|
+
} catch {
|
|
17782
|
+
return { ok: false, message: t("cli.licenseJson"), reason: "invalid-license" };
|
|
17783
|
+
}
|
|
17633
17784
|
let response;
|
|
17634
17785
|
try {
|
|
17635
|
-
response = await fetch(`${baseUrl}
|
|
17786
|
+
response = await (options.fetchFn ?? fetch)(`${baseUrl}/${action === "activate" ? "devices" : "refresh"}`, {
|
|
17636
17787
|
method: "POST",
|
|
17637
17788
|
headers: { "content-type": "application/json" },
|
|
17638
|
-
body:
|
|
17789
|
+
body: requestBody,
|
|
17790
|
+
redirect: "error",
|
|
17639
17791
|
signal: AbortSignal.timeout(15e3)
|
|
17640
17792
|
});
|
|
17641
17793
|
} catch {
|
|
@@ -17650,6 +17802,25 @@ async function refreshLicense(options = {}) {
|
|
|
17650
17802
|
let inactive = false;
|
|
17651
17803
|
try {
|
|
17652
17804
|
const body = await response.json();
|
|
17805
|
+
if (response.status === 409 && body.code === "device_limit") {
|
|
17806
|
+
const devices2 = parseDevices(body.devices);
|
|
17807
|
+
if (devices2)
|
|
17808
|
+
return {
|
|
17809
|
+
ok: false,
|
|
17810
|
+
message: t("cli.licenseDeviceLimit"),
|
|
17811
|
+
reason: "device_limit",
|
|
17812
|
+
devices: devices2
|
|
17813
|
+
};
|
|
17814
|
+
}
|
|
17815
|
+
if (response.status === 403 && body.code === "device_revoked") {
|
|
17816
|
+
const devices2 = parseDevices(body.devices);
|
|
17817
|
+
return {
|
|
17818
|
+
ok: false,
|
|
17819
|
+
message: t("cli.licenseDeviceRevoked"),
|
|
17820
|
+
reason: "device_revoked",
|
|
17821
|
+
...devices2 ? { devices: devices2 } : {}
|
|
17822
|
+
};
|
|
17823
|
+
}
|
|
17653
17824
|
if (typeof body.error === "string")
|
|
17654
17825
|
detail = body.error;
|
|
17655
17826
|
inactive = body.reason === "inactive";
|
|
@@ -17680,13 +17851,14 @@ async function refreshLicense(options = {}) {
|
|
|
17680
17851
|
}
|
|
17681
17852
|
const next = `${JSON.stringify({ token: entitlement.token, signature: entitlement.signature })}
|
|
17682
17853
|
`;
|
|
17683
|
-
const publicKeyPem = options.publicKeyPem ?? PROD_PUBLIC_KEY_PEM;
|
|
17684
17854
|
if (publicKeyPem !== "") {
|
|
17685
17855
|
const status3 = verifyEntitlement(next, options.now ?? /* @__PURE__ */ new Date(), publicKeyPem);
|
|
17686
|
-
if (status3.state !== "active" && status3.state !== "grace") {
|
|
17856
|
+
if (status3.state !== "active" && status3.state !== "grace" || status3.claims?.dev !== device.id) {
|
|
17687
17857
|
return {
|
|
17688
17858
|
ok: false,
|
|
17689
|
-
message: t("cli.refreshInvalid", {
|
|
17859
|
+
message: t("cli.refreshInvalid", {
|
|
17860
|
+
detail: status3.claims?.dev !== device.id ? t("cli.licenseOtherDevice") : status3.detail
|
|
17861
|
+
}),
|
|
17690
17862
|
reason: "invalid-response"
|
|
17691
17863
|
};
|
|
17692
17864
|
}
|
|
@@ -17719,7 +17891,70 @@ async function refreshLicense(options = {}) {
|
|
|
17719
17891
|
reason: "write"
|
|
17720
17892
|
};
|
|
17721
17893
|
}
|
|
17722
|
-
|
|
17894
|
+
const devices = parseDevices(entitlement.devices);
|
|
17895
|
+
return {
|
|
17896
|
+
ok: true,
|
|
17897
|
+
message: t("cli.licenseRenewed", { suffix: expSuffix(entitlement.token) }),
|
|
17898
|
+
...devices ? { devices } : {}
|
|
17899
|
+
};
|
|
17900
|
+
}
|
|
17901
|
+
async function manageLicenseDevices(options, action, id2) {
|
|
17902
|
+
const baseUrl = options.baseUrl ?? process.env.WINGMAN_LICENSE_SERVER ?? DEFAULT_LICENSE_SERVER;
|
|
17903
|
+
if (!licenseNetworkAllowed(options, baseUrl))
|
|
17904
|
+
return { ok: false, message: t("cli.licenseFixturesOffline"), reason: "offline" };
|
|
17905
|
+
let raw;
|
|
17906
|
+
try {
|
|
17907
|
+
raw = readFileSync4(join8(options.homeDir ?? homedir4(), ".wingman", "license.json"), "utf8");
|
|
17908
|
+
} catch {
|
|
17909
|
+
return { ok: false, message: t("cli.licenseMissing"), reason: "missing" };
|
|
17910
|
+
}
|
|
17911
|
+
const key = options.publicKeyPem ?? PROD_PUBLIC_KEY_PEM;
|
|
17912
|
+
const status3 = key === "" ? void 0 : verifyEntitlement(raw, options.now ?? /* @__PURE__ */ new Date(), key);
|
|
17913
|
+
if (status3?.state === "invalid")
|
|
17914
|
+
return { ok: false, message: t("cli.licenseSignature"), reason: "invalid-license" };
|
|
17915
|
+
let callerDeviceId;
|
|
17916
|
+
if (action === "unbind") {
|
|
17917
|
+
const device = options.device === void 0 ? getLicenseDevice() : options.device;
|
|
17918
|
+
if (!device)
|
|
17919
|
+
return {
|
|
17920
|
+
ok: false,
|
|
17921
|
+
message: t("cli.licenseDeviceUnavailable"),
|
|
17922
|
+
reason: "device-unavailable"
|
|
17923
|
+
};
|
|
17924
|
+
if (status3?.state !== "active" || status3.claims?.dev !== device.id)
|
|
17925
|
+
return {
|
|
17926
|
+
ok: false,
|
|
17927
|
+
message: t("cli.licenseUnbindRequiresActiveDevice"),
|
|
17928
|
+
reason: "invalid-license"
|
|
17929
|
+
};
|
|
17930
|
+
callerDeviceId = device.id;
|
|
17931
|
+
}
|
|
17932
|
+
try {
|
|
17933
|
+
const response = await (options.fetchFn ?? fetch)(`${baseUrl}/devices`, {
|
|
17934
|
+
method: "POST",
|
|
17935
|
+
headers: { "content-type": "application/json" },
|
|
17936
|
+
body: JSON.stringify({
|
|
17937
|
+
license: JSON.parse(raw),
|
|
17938
|
+
action,
|
|
17939
|
+
...id2 ? { id: id2 } : {},
|
|
17940
|
+
...callerDeviceId === void 0 ? {} : { device: { id: callerDeviceId } }
|
|
17941
|
+
}),
|
|
17942
|
+
signal: AbortSignal.timeout(15e3),
|
|
17943
|
+
redirect: "error"
|
|
17944
|
+
});
|
|
17945
|
+
if (!response.ok)
|
|
17946
|
+
return {
|
|
17947
|
+
ok: false,
|
|
17948
|
+
message: response.status === 403 && action === "unbind" ? t("cli.licenseUnbindRequiresActiveDevice") : t("cli.refreshHttp", { status: response.status }),
|
|
17949
|
+
reason: response.status === 403 ? "invalid-license" : "server"
|
|
17950
|
+
};
|
|
17951
|
+
const devices = parseDevices((await response.json()).devices);
|
|
17952
|
+
if (!devices)
|
|
17953
|
+
return { ok: false, message: t("cli.licenseDevicesInvalid"), reason: "invalid-response" };
|
|
17954
|
+
return { ok: true, message: "", devices };
|
|
17955
|
+
} catch {
|
|
17956
|
+
return { ok: false, message: t("cli.refreshNetwork"), reason: "network" };
|
|
17957
|
+
}
|
|
17723
17958
|
}
|
|
17724
17959
|
function expSuffix(token) {
|
|
17725
17960
|
try {
|
|
@@ -17949,9 +18184,9 @@ function runSecurity(args) {
|
|
|
17949
18184
|
// apps/cli/dist/mobile/snapshot.js
|
|
17950
18185
|
init_dist2();
|
|
17951
18186
|
init_dist();
|
|
17952
|
-
import { createHash as
|
|
18187
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
17953
18188
|
function mobileId(value) {
|
|
17954
|
-
return
|
|
18189
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
17955
18190
|
}
|
|
17956
18191
|
function buildMobileSnapshot(narrative, at, wired) {
|
|
17957
18192
|
const sources = narrative.getProjects().projects;
|
|
@@ -18194,6 +18429,7 @@ function writeMobileState(homeDir, state) {
|
|
|
18194
18429
|
}
|
|
18195
18430
|
|
|
18196
18431
|
// apps/cli/dist/mobile/service.js
|
|
18432
|
+
var PAIRING_WINDOW_MS = 5 * 6e4;
|
|
18197
18433
|
function createMobileService(options) {
|
|
18198
18434
|
const keychain = options.keychain ?? createMacKeychain();
|
|
18199
18435
|
const fetchRelay = options.fetch ?? globalThis.fetch;
|
|
@@ -18678,7 +18914,7 @@ function createMobileService(options) {
|
|
|
18678
18914
|
throw new Error("mobile.relayUnavailable");
|
|
18679
18915
|
created = true;
|
|
18680
18916
|
persist((current) => [...current, pair]);
|
|
18681
|
-
pairs.set(pairId, { ...runtime(pair), pairingUntil: now().getTime() +
|
|
18917
|
+
pairs.set(pairId, { ...runtime(pair), pairingUntil: now().getTime() + PAIRING_WINDOW_MS });
|
|
18682
18918
|
stateError = void 0;
|
|
18683
18919
|
return { pairId, url: pairingUrl({ relay, pairId, deviceKey }) };
|
|
18684
18920
|
} catch {
|
|
@@ -18702,16 +18938,15 @@ function createMobileService(options) {
|
|
|
18702
18938
|
throw new Error(created ? "mobile.localWriteFailed" : "mobile.relayUnavailable");
|
|
18703
18939
|
}
|
|
18704
18940
|
},
|
|
18705
|
-
async list(
|
|
18706
|
-
const entry = pairing ? pairs.get(pairing) : void 0;
|
|
18707
|
-
if (entry?.status === "pending")
|
|
18708
|
-
entry.pairingUntil = now().getTime() + 1e4;
|
|
18941
|
+
async list() {
|
|
18709
18942
|
await refresh();
|
|
18710
|
-
const
|
|
18943
|
+
const at = now().getTime();
|
|
18944
|
+
const error = stateError ?? [...pairs.values()].find((entry) => entry.error)?.error;
|
|
18711
18945
|
return {
|
|
18712
|
-
pairs: [...pairs.values()].map((
|
|
18713
|
-
...
|
|
18714
|
-
status:
|
|
18946
|
+
pairs: [...pairs.values()].map((entry) => ({
|
|
18947
|
+
...entry.pair,
|
|
18948
|
+
status: entry.status,
|
|
18949
|
+
...entry.status === "pending" && at >= (entry.pairingUntil ?? 0) ? { expired: true } : {},
|
|
18715
18950
|
...!options.licensed() ? { paused: true } : {}
|
|
18716
18951
|
})),
|
|
18717
18952
|
...error ? { error } : {}
|
|
@@ -20977,7 +21212,7 @@ function resolveWingmanVersion({ defined, moduleUrl = import.meta.url, readPacka
|
|
|
20977
21212
|
return void 0;
|
|
20978
21213
|
}
|
|
20979
21214
|
function wingmanVersion() {
|
|
20980
|
-
const defined = false ? void 0 : "0.9.
|
|
21215
|
+
const defined = false ? void 0 : "0.9.8";
|
|
20981
21216
|
return resolveWingmanVersion({
|
|
20982
21217
|
defined,
|
|
20983
21218
|
readPackage: (url) => JSON.parse(readFileSync18(url, "utf8"))
|
|
@@ -22766,12 +23001,12 @@ function humanFeedbackState(harness2, session2) {
|
|
|
22766
23001
|
return state;
|
|
22767
23002
|
return state.state === "needs_user" ? { ...state, since: at } : { ...state, requestSince: at };
|
|
22768
23003
|
}
|
|
22769
|
-
function humanWaiting(harness2, lines2,
|
|
22770
|
-
if (harness2 !== "codex" ||
|
|
22771
|
-
return
|
|
23004
|
+
function humanWaiting(harness2, lines2, wait2, at = wait2.since) {
|
|
23005
|
+
if (harness2 !== "codex" || wait2.kind !== "permission")
|
|
23006
|
+
return wait2;
|
|
22772
23007
|
if (codexAutomaticReviewAt(lines2, at))
|
|
22773
23008
|
return null;
|
|
22774
|
-
return codexAutomaticReviewAt(lines2,
|
|
23009
|
+
return codexAutomaticReviewAt(lines2, wait2.since) ? { ...wait2, since: at } : wait2;
|
|
22775
23010
|
}
|
|
22776
23011
|
function observedMcpServers(lines2, configuredServers) {
|
|
22777
23012
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -22837,7 +23072,7 @@ function observedSkills(lines2) {
|
|
|
22837
23072
|
function derive(harness2, session2, nowIso, configuredPlugins, configuredMcpServers) {
|
|
22838
23073
|
const codexLines = sortedCodexByTimestamp(session2.codexLines);
|
|
22839
23074
|
const waiting2 = session2.waiting === null ? null : humanWaiting(harness2, codexLines, session2.waiting, session2.feedbackRequestAt);
|
|
22840
|
-
const waitEpisodes = session2.waitEpisodes.map((
|
|
23075
|
+
const waitEpisodes = session2.waitEpisodes.map((wait2) => humanWaiting(harness2, codexLines, wait2, wait2.requestAt)).filter((wait2) => wait2 !== null);
|
|
22841
23076
|
const lines2 = harness2 === "codex" ? adaptCodexNarrativeLines(codexLines) : sortedByTimestamp(session2.lines);
|
|
22842
23077
|
const timeline = deriveTodoTimeline(extractTodoSnapshots(lines2));
|
|
22843
23078
|
const allSteps = extractSteps(lines2);
|
|
@@ -24082,9 +24317,132 @@ import { execFile as execFile3 } from "node:child_process";
|
|
|
24082
24317
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
24083
24318
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
24084
24319
|
import { createServer } from "node:http";
|
|
24085
|
-
import { hostname } from "node:os";
|
|
24320
|
+
import { hostname as hostname2 } from "node:os";
|
|
24086
24321
|
import { extname, isAbsolute as isAbsolute7, relative as relative5, resolve as resolve14, sep as sep2 } from "node:path";
|
|
24087
24322
|
|
|
24323
|
+
// apps/cli/dist/license-activation.js
|
|
24324
|
+
import { setTimeout as wait } from "node:timers/promises";
|
|
24325
|
+
function createLicenseActivation(options) {
|
|
24326
|
+
let activation = { state: "pending", devices: [], maxDevices: 3 };
|
|
24327
|
+
let queue = Promise.resolve();
|
|
24328
|
+
const serial = (work) => {
|
|
24329
|
+
const next = queue.then(work, work);
|
|
24330
|
+
queue = next.catch(() => {
|
|
24331
|
+
});
|
|
24332
|
+
return next;
|
|
24333
|
+
};
|
|
24334
|
+
const statusOptions = () => options;
|
|
24335
|
+
const status3 = () => {
|
|
24336
|
+
const current = licenseStatus(statusOptions());
|
|
24337
|
+
return {
|
|
24338
|
+
state: current.state,
|
|
24339
|
+
detail: current.detail,
|
|
24340
|
+
...current.claims ? { plan: current.claims.plan, exp: current.claims.exp } : {},
|
|
24341
|
+
tier: checkLicense(statusOptions()) ? "full" : "free",
|
|
24342
|
+
...current.state === "none" || activation.state === "pending" && current.claims?.dev ? {} : { activation }
|
|
24343
|
+
};
|
|
24344
|
+
};
|
|
24345
|
+
const refresh = async (explicitActivation = false) => {
|
|
24346
|
+
const result5 = await (explicitActivation ? activateLicense : refreshLicense)(options);
|
|
24347
|
+
if (result5.reason === "device_limit")
|
|
24348
|
+
activation = {
|
|
24349
|
+
state: activation.state === "device_revoked" ? "device_revoked" : "device_limit",
|
|
24350
|
+
devices: result5.devices ?? [],
|
|
24351
|
+
maxDevices: 3,
|
|
24352
|
+
detail: result5.message
|
|
24353
|
+
};
|
|
24354
|
+
else if (result5.reason === "device_revoked")
|
|
24355
|
+
activation = {
|
|
24356
|
+
state: "device_revoked",
|
|
24357
|
+
devices: result5.devices ?? activation.devices,
|
|
24358
|
+
maxDevices: 3,
|
|
24359
|
+
detail: result5.message
|
|
24360
|
+
};
|
|
24361
|
+
else if (result5.reason === "device-unavailable")
|
|
24362
|
+
activation = { state: "unavailable", devices: [], maxDevices: 3, detail: result5.message };
|
|
24363
|
+
else if (result5.ok) {
|
|
24364
|
+
const current = licenseStatus(statusOptions());
|
|
24365
|
+
activation = {
|
|
24366
|
+
state: current.claims?.dev && result5.devices ? "active" : "pending",
|
|
24367
|
+
devices: result5.devices ?? [],
|
|
24368
|
+
maxDevices: 3
|
|
24369
|
+
};
|
|
24370
|
+
}
|
|
24371
|
+
return result5;
|
|
24372
|
+
};
|
|
24373
|
+
return {
|
|
24374
|
+
status: status3,
|
|
24375
|
+
refresh: () => serial(() => refresh()),
|
|
24376
|
+
autoRefresh: () => serial(async () => {
|
|
24377
|
+
if ((options.publicKeyPem ?? PROD_PUBLIC_KEY_PEM) === "")
|
|
24378
|
+
return;
|
|
24379
|
+
const result5 = await refresh();
|
|
24380
|
+
return result5.ok || result5.reason === "missing" ? void 0 : result5.reason;
|
|
24381
|
+
}),
|
|
24382
|
+
install: (raw) => serial(async () => {
|
|
24383
|
+
const result5 = installLicense(raw, statusOptions());
|
|
24384
|
+
if (!result5.ok)
|
|
24385
|
+
return { ok: false, message: result5.error };
|
|
24386
|
+
activation = { state: "pending", devices: [], maxDevices: 3 };
|
|
24387
|
+
if ((options.publicKeyPem ?? PROD_PUBLIC_KEY_PEM) !== "")
|
|
24388
|
+
await refresh(true);
|
|
24389
|
+
return { ok: true, message: "" };
|
|
24390
|
+
}),
|
|
24391
|
+
devices: (action, id2) => serial(async () => {
|
|
24392
|
+
if (action === "activate")
|
|
24393
|
+
return refresh(true);
|
|
24394
|
+
const result5 = await manageLicenseDevices(options, action, id2);
|
|
24395
|
+
if (!result5.ok)
|
|
24396
|
+
return result5;
|
|
24397
|
+
activation = { ...activation, devices: result5.devices ?? [] };
|
|
24398
|
+
if (action === "unbind") {
|
|
24399
|
+
await (options.wait ?? wait)(1e3);
|
|
24400
|
+
return refresh();
|
|
24401
|
+
}
|
|
24402
|
+
const current = licenseStatus(statusOptions());
|
|
24403
|
+
if (activation.state !== "device_revoked" && current.claims?.dev && (current.state === "active" || current.state === "grace")) {
|
|
24404
|
+
const devices = result5.devices ?? [];
|
|
24405
|
+
activation = {
|
|
24406
|
+
state: devices.some((device) => device.id === current.claims?.dev) ? "active" : "device_limit",
|
|
24407
|
+
devices,
|
|
24408
|
+
maxDevices: 3
|
|
24409
|
+
};
|
|
24410
|
+
}
|
|
24411
|
+
return result5;
|
|
24412
|
+
})
|
|
24413
|
+
};
|
|
24414
|
+
}
|
|
24415
|
+
|
|
24416
|
+
// apps/cli/dist/license-auto-refresh.js
|
|
24417
|
+
function startLicenseAutoRefresh(options) {
|
|
24418
|
+
const clock = options.clock ?? { setTimeout, clearTimeout, setInterval, clearInterval };
|
|
24419
|
+
let running = false;
|
|
24420
|
+
const run = async () => {
|
|
24421
|
+
if (running)
|
|
24422
|
+
return;
|
|
24423
|
+
running = true;
|
|
24424
|
+
try {
|
|
24425
|
+
await options.refresh();
|
|
24426
|
+
} catch {
|
|
24427
|
+
options.warn?.("license automatic refresh failed");
|
|
24428
|
+
} finally {
|
|
24429
|
+
running = false;
|
|
24430
|
+
}
|
|
24431
|
+
};
|
|
24432
|
+
let interval;
|
|
24433
|
+
const timeout = clock.setTimeout(() => {
|
|
24434
|
+
void run();
|
|
24435
|
+
interval = clock.setInterval(() => void run(), 24 * 60 * 60 * 1e3);
|
|
24436
|
+
interval.unref?.();
|
|
24437
|
+
}, 5e3);
|
|
24438
|
+
timeout.unref?.();
|
|
24439
|
+
return () => {
|
|
24440
|
+
clock.clearTimeout(timeout);
|
|
24441
|
+
if (interval !== void 0)
|
|
24442
|
+
clock.clearInterval(interval);
|
|
24443
|
+
};
|
|
24444
|
+
}
|
|
24445
|
+
|
|
24088
24446
|
// apps/cli/dist/suggest.js
|
|
24089
24447
|
init_dist2();
|
|
24090
24448
|
init_dist();
|
|
@@ -24271,10 +24629,21 @@ var PANEL_ACTION_HEADER = "x-wingman-panel-action";
|
|
|
24271
24629
|
var LOCAL_HOST = /^(?:127\.0\.0\.1|localhost)(?::\d+)?$|^\[::1\](?::\d+)?$/i;
|
|
24272
24630
|
function createWingmanServer(options) {
|
|
24273
24631
|
const warn = options.warn ?? ((message) => console.error(message));
|
|
24274
|
-
const
|
|
24632
|
+
const licenseOptions = {
|
|
24633
|
+
...options.homeDir === void 0 ? {} : { homeDir: options.homeDir },
|
|
24634
|
+
...options.licenseOptions,
|
|
24635
|
+
...options.licenseRefreshOptions,
|
|
24636
|
+
...options.readonlyActions && options.licenseOptions?.device === void 0 && options.licenseRefreshOptions?.device === void 0 ? { device: null } : {},
|
|
24637
|
+
offline: options.readonlyActions === true || options.licenseRefreshOptions?.offline === true
|
|
24638
|
+
};
|
|
24639
|
+
options = { ...options, licenseOptions };
|
|
24640
|
+
const runtime = {
|
|
24641
|
+
anatomy: null,
|
|
24642
|
+
license: createLicenseActivation(licenseOptions)
|
|
24643
|
+
};
|
|
24275
24644
|
options.narrative?.setCodexPluginsProvider(() => runtime.anatomy?.configs.find((config) => config.harness === "codex")?.slots.skills.filter((item) => item.nativeName.startsWith("plugins.")).map((item) => item.nativeName.slice("plugins.".length)) ?? []);
|
|
24276
24645
|
options.narrative?.setCcMcpServersProvider(() => runtime.anatomy?.configs.find((config) => config.harness === "cc")?.slots.tools.filter((item) => item.nativeName.startsWith("mcpServers.")).map((item) => item.nativeName.slice("mcpServers.".length)) ?? []);
|
|
24277
|
-
|
|
24646
|
+
const server = createServer({ requireHostHeader: false }, (req, res) => {
|
|
24278
24647
|
handle(req, res, options, warn, runtime).catch((error) => {
|
|
24279
24648
|
warn(t("cli.server.requestFailed", { error: errorMessage3(error) }));
|
|
24280
24649
|
if (!res.headersSent)
|
|
@@ -24283,6 +24652,21 @@ function createWingmanServer(options) {
|
|
|
24283
24652
|
res.end();
|
|
24284
24653
|
});
|
|
24285
24654
|
});
|
|
24655
|
+
if (options.licenseAutoRefresh) {
|
|
24656
|
+
server.once("listening", () => {
|
|
24657
|
+
const stop = startLicenseAutoRefresh({
|
|
24658
|
+
...options.licenseAutoRefresh,
|
|
24659
|
+
refresh: async () => {
|
|
24660
|
+
const reason = await runtime.license.autoRefresh();
|
|
24661
|
+
if (reason)
|
|
24662
|
+
warn(`license automatic refresh: ${reason}`);
|
|
24663
|
+
},
|
|
24664
|
+
warn
|
|
24665
|
+
});
|
|
24666
|
+
server.once("close", stop);
|
|
24667
|
+
});
|
|
24668
|
+
}
|
|
24669
|
+
return server;
|
|
24286
24670
|
}
|
|
24287
24671
|
function listen(server, port) {
|
|
24288
24672
|
return new Promise((resolvePort, reject) => {
|
|
@@ -24382,7 +24766,7 @@ async function handle(req, res, options, warn, runtime) {
|
|
|
24382
24766
|
return;
|
|
24383
24767
|
}
|
|
24384
24768
|
if (url.pathname === "/api/license/install" && req.method === "POST") {
|
|
24385
|
-
await handleLicenseInstall(req, res, options);
|
|
24769
|
+
await handleLicenseInstall(req, res, options, runtime);
|
|
24386
24770
|
return;
|
|
24387
24771
|
}
|
|
24388
24772
|
if (url.pathname === "/api/license/refresh") {
|
|
@@ -24394,7 +24778,36 @@ async function handle(req, res, options, warn, runtime) {
|
|
|
24394
24778
|
sendJson2(res, 403, { error: t("cli.server.refreshFromPanel") });
|
|
24395
24779
|
return;
|
|
24396
24780
|
}
|
|
24397
|
-
await handleLicenseRefresh(res, options);
|
|
24781
|
+
await handleLicenseRefresh(res, options, runtime);
|
|
24782
|
+
return;
|
|
24783
|
+
}
|
|
24784
|
+
if (url.pathname === "/api/license/devices") {
|
|
24785
|
+
if (req.method !== "POST") {
|
|
24786
|
+
sendText2(res, 405, "method not allowed");
|
|
24787
|
+
return;
|
|
24788
|
+
}
|
|
24789
|
+
if (req.headers[PANEL_ACTION_HEADER] !== "license-devices") {
|
|
24790
|
+
sendJson2(res, 403, { error: t("cli.server.refreshFromPanel") });
|
|
24791
|
+
return;
|
|
24792
|
+
}
|
|
24793
|
+
const raw = await readTextBody(req, LICENSE_BODY_LIMIT);
|
|
24794
|
+
let body;
|
|
24795
|
+
try {
|
|
24796
|
+
body = JSON.parse(raw ?? "");
|
|
24797
|
+
} catch {
|
|
24798
|
+
sendJson2(res, 400, { error: "invalid device action" });
|
|
24799
|
+
return;
|
|
24800
|
+
}
|
|
24801
|
+
if (!body || body.action !== "list" && body.action !== "unbind" && body.action !== "activate" || body.action === "unbind" && (typeof body.id !== "string" || !body.id)) {
|
|
24802
|
+
sendJson2(res, 400, { error: "invalid device action" });
|
|
24803
|
+
return;
|
|
24804
|
+
}
|
|
24805
|
+
const result5 = await runtime.license.devices(body.action, typeof body.id === "string" ? body.id : void 0);
|
|
24806
|
+
sendJson2(res, result5.ok ? 200 : result5.reason === "device_limit" ? 409 : result5.reason === "device_revoked" || result5.reason === "invalid-license" ? 403 : 400, {
|
|
24807
|
+
...licenseApiStatus(options, runtime),
|
|
24808
|
+
...result5.ok ? {} : { error: result5.message },
|
|
24809
|
+
...result5.reason === "device_revoked" ? { code: result5.reason } : {}
|
|
24810
|
+
});
|
|
24398
24811
|
return;
|
|
24399
24812
|
}
|
|
24400
24813
|
if (req.method !== "GET") {
|
|
@@ -24402,19 +24815,7 @@ async function handle(req, res, options, warn, runtime) {
|
|
|
24402
24815
|
return;
|
|
24403
24816
|
}
|
|
24404
24817
|
if (url.pathname === "/api/license") {
|
|
24405
|
-
const body =
|
|
24406
|
-
body.tier = checkLicense(options.licenseOptions) ? "full" : "free";
|
|
24407
|
-
if (body.tier === "free") {
|
|
24408
|
-
const cached = runtime.anatomy?.configs;
|
|
24409
|
-
const detected = new Set((cached ?? []).map((config) => config.harness));
|
|
24410
|
-
const homeDir = options.homeDir ?? options.licenseOptions?.homeDir;
|
|
24411
|
-
const settings = homeDir === void 0 ? void 0 : readReturnTargetSettings(homeDir);
|
|
24412
|
-
const selected = settings && !("unavailable" in settings) ? settings.freeHarness : void 0;
|
|
24413
|
-
body.freeHarness = selected !== void 0 && (cached === void 0 || detected.has(selected)) ? selected : HARNESS_IDS.find((harness2) => detected.has(harness2)) ?? HARNESS_IDS[0];
|
|
24414
|
-
if (settings && !("unavailable" in settings) && settings.freeHarnessChosenAt !== void 0) {
|
|
24415
|
-
body.freeHarnessSwitchableAt = new Date(Date.parse(settings.freeHarnessChosenAt) + FREE_HARNESS_SWITCH_COOLDOWN_MS).toISOString();
|
|
24416
|
-
}
|
|
24417
|
-
}
|
|
24818
|
+
const body = licenseApiStatus(options, runtime);
|
|
24418
24819
|
sendJson2(res, 200, body);
|
|
24419
24820
|
return;
|
|
24420
24821
|
}
|
|
@@ -24709,7 +25110,7 @@ function handleProtocolUserAction(body, res, options, runtime) {
|
|
|
24709
25110
|
...install && before.version !== void 0 && after.version !== void 0 && before.version < after.version ? { fromVersion: before.version, toVersion: after.version } : {},
|
|
24710
25111
|
blockBytes: Buffer.byteLength(block ?? ""),
|
|
24711
25112
|
backupDir: t("cli.server.backupPreview"),
|
|
24712
|
-
hostname:
|
|
25113
|
+
hostname: hostname2()
|
|
24713
25114
|
});
|
|
24714
25115
|
} else {
|
|
24715
25116
|
runtime.anatomy = null;
|
|
@@ -24889,56 +25290,45 @@ async function handleTaskOverlay(req, res, options) {
|
|
|
24889
25290
|
sendJson2(res, 200, { key, entry: result5.entry });
|
|
24890
25291
|
}
|
|
24891
25292
|
var LICENSE_BODY_LIMIT = 64 * 1024;
|
|
24892
|
-
|
|
25293
|
+
function licenseApiStatus(options, runtime) {
|
|
25294
|
+
const body = runtime.license.status();
|
|
25295
|
+
if (body.tier === "free") {
|
|
25296
|
+
const cached = runtime.anatomy?.configs;
|
|
25297
|
+
const detected = new Set((cached ?? []).map((config) => config.harness));
|
|
25298
|
+
const homeDir = options.homeDir ?? options.licenseOptions?.homeDir;
|
|
25299
|
+
const settings = homeDir === void 0 ? void 0 : readReturnTargetSettings(homeDir);
|
|
25300
|
+
const selected = settings && !("unavailable" in settings) ? settings.freeHarness : void 0;
|
|
25301
|
+
body.freeHarness = selected !== void 0 && (cached === void 0 || detected.has(selected)) ? selected : HARNESS_IDS.find((harness2) => detected.has(harness2)) ?? HARNESS_IDS[0];
|
|
25302
|
+
if (settings && !("unavailable" in settings) && settings.freeHarnessChosenAt !== void 0) {
|
|
25303
|
+
body.freeHarnessSwitchableAt = new Date(Date.parse(settings.freeHarnessChosenAt) + FREE_HARNESS_SWITCH_COOLDOWN_MS).toISOString();
|
|
25304
|
+
}
|
|
25305
|
+
}
|
|
25306
|
+
return body;
|
|
25307
|
+
}
|
|
25308
|
+
async function handleLicenseInstall(req, res, options, runtime) {
|
|
24893
25309
|
const raw = await readTextBody(req, LICENSE_BODY_LIMIT);
|
|
24894
25310
|
if (raw === void 0) {
|
|
24895
25311
|
sendJson2(res, 400, { error: t("cli.server.licenseRead") });
|
|
24896
25312
|
return;
|
|
24897
25313
|
}
|
|
24898
|
-
const result5 =
|
|
25314
|
+
const result5 = await runtime.license.install(raw);
|
|
24899
25315
|
if (!result5.ok) {
|
|
24900
|
-
sendJson2(res, 400, { error: result5.
|
|
25316
|
+
sendJson2(res, 400, { error: result5.message });
|
|
24901
25317
|
return;
|
|
24902
25318
|
}
|
|
24903
|
-
sendJson2(res, 200,
|
|
25319
|
+
sendJson2(res, 200, licenseApiStatus(options, runtime));
|
|
24904
25320
|
}
|
|
24905
|
-
async function handleLicenseRefresh(res, options) {
|
|
24906
|
-
const
|
|
24907
|
-
const publicKeyPem = options.licenseRefreshOptions?.publicKeyPem ?? options.licenseOptions?.publicKeyPem;
|
|
24908
|
-
const now = options.licenseRefreshOptions?.now ?? options.licenseOptions?.now;
|
|
24909
|
-
const refreshOptions = {
|
|
24910
|
-
...options.licenseRefreshOptions,
|
|
24911
|
-
...homeDir === void 0 ? {} : { homeDir },
|
|
24912
|
-
...publicKeyPem === void 0 ? {} : { publicKeyPem },
|
|
24913
|
-
...now === void 0 ? {} : { now }
|
|
24914
|
-
};
|
|
24915
|
-
const result5 = await refreshLicense(refreshOptions);
|
|
25321
|
+
async function handleLicenseRefresh(res, options, runtime) {
|
|
25322
|
+
const result5 = await runtime.license.refresh();
|
|
24916
25323
|
if (!result5.ok) {
|
|
24917
|
-
sendJson2(res, 400, {
|
|
25324
|
+
sendJson2(res, result5.reason === "device_limit" ? 409 : result5.reason === "device_revoked" ? 403 : 400, {
|
|
24918
25325
|
error: result5.message,
|
|
24919
|
-
canSubscribe: result5.reason === "inactive"
|
|
25326
|
+
canSubscribe: result5.reason === "inactive",
|
|
25327
|
+
...result5.reason === "device_revoked" ? { code: result5.reason } : {}
|
|
24920
25328
|
});
|
|
24921
25329
|
return;
|
|
24922
25330
|
}
|
|
24923
|
-
|
|
24924
|
-
...options.licenseOptions,
|
|
24925
|
-
...homeDir === void 0 ? {} : { homeDir },
|
|
24926
|
-
...publicKeyPem === void 0 ? {} : { publicKeyPem },
|
|
24927
|
-
...now === void 0 ? {} : { now }
|
|
24928
|
-
};
|
|
24929
|
-
sendJson2(res, 200, {
|
|
24930
|
-
...publicLicenseStatus(licenseStatus(statusOptions)),
|
|
24931
|
-
tier: "full",
|
|
24932
|
-
message: result5.message
|
|
24933
|
-
});
|
|
24934
|
-
}
|
|
24935
|
-
function publicLicenseStatus(status3) {
|
|
24936
|
-
const body = { state: status3.state, detail: status3.detail };
|
|
24937
|
-
if (status3.claims) {
|
|
24938
|
-
body.plan = status3.claims.plan;
|
|
24939
|
-
body.exp = status3.claims.exp;
|
|
24940
|
-
}
|
|
24941
|
-
return body;
|
|
25331
|
+
sendJson2(res, 200, { ...licenseApiStatus(options, runtime), message: result5.message });
|
|
24942
25332
|
}
|
|
24943
25333
|
function readTextBody(req, maxBytes) {
|
|
24944
25334
|
return new Promise((resolveBody) => {
|
|
@@ -25161,7 +25551,7 @@ async function main(argv) {
|
|
|
25161
25551
|
return 0;
|
|
25162
25552
|
}
|
|
25163
25553
|
if (rest[0] === "refresh") {
|
|
25164
|
-
const result5 = await refreshLicense({ homeDir });
|
|
25554
|
+
const result5 = await refreshLicense({ homeDir, offline: argv.includes("--fixtures") });
|
|
25165
25555
|
process.stdout.write(`${result5.message}
|
|
25166
25556
|
`);
|
|
25167
25557
|
if (!result5.ok)
|
|
@@ -25271,7 +25661,10 @@ async function runServe(args) {
|
|
|
25271
25661
|
receiver: createReceiver({ onEvent: narrative.handleEvent }),
|
|
25272
25662
|
readonlyActions: fixtures,
|
|
25273
25663
|
homeDir,
|
|
25664
|
+
// fixtures 只替换扫描根;授权仍使用进程 HOME,测试由子进程环境注入临时目录。
|
|
25665
|
+
licenseOptions: { homeDir: homedir17() },
|
|
25274
25666
|
narrative,
|
|
25667
|
+
licenseAutoRefresh: {},
|
|
25275
25668
|
...mobile === void 0 ? {} : { mobile },
|
|
25276
25669
|
hookToken,
|
|
25277
25670
|
...locale === void 0 ? {} : { localeArg: locale },
|