@primitive.ai/prim 0.1.0-alpha.74 → 0.1.0-alpha.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hook-runtime/index.js +439 -400
- package/dist/index.js +423 -384
- package/package.json +1 -1
|
@@ -20473,6 +20473,23 @@ function decisionIngestionStatus(cwd) {
|
|
|
20473
20473
|
return isRepoActiveForCapture(cwd) ? "enabled" : "disabled";
|
|
20474
20474
|
}
|
|
20475
20475
|
|
|
20476
|
+
// src/lib/confirmation.ts
|
|
20477
|
+
function isNonInteractive(globals) {
|
|
20478
|
+
return Boolean(globals.nonInteractive || process.env.CI || process.env.PRIM_NON_INTERACTIVE);
|
|
20479
|
+
}
|
|
20480
|
+
async function askConfirmation(question, output = process.stdout) {
|
|
20481
|
+
if (!process.stdin.isTTY) return false;
|
|
20482
|
+
const { createInterface } = await import("readline/promises");
|
|
20483
|
+
const rl = createInterface({ input: process.stdin, output });
|
|
20484
|
+
try {
|
|
20485
|
+
const answer = await rl.question(`${question} [y/N] `);
|
|
20486
|
+
const normalized = answer.trim().toLowerCase();
|
|
20487
|
+
return normalized === "y" || normalized === "yes";
|
|
20488
|
+
} finally {
|
|
20489
|
+
rl.close();
|
|
20490
|
+
}
|
|
20491
|
+
}
|
|
20492
|
+
|
|
20476
20493
|
// src/lib/post-commit-hook.ts
|
|
20477
20494
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
20478
20495
|
import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
|
|
@@ -22353,85 +22370,72 @@ function printJson(data) {
|
|
|
22353
22370
|
console.log(JSON.stringify(data, null, 2));
|
|
22354
22371
|
}
|
|
22355
22372
|
|
|
22356
|
-
// src/
|
|
22357
|
-
|
|
22358
|
-
|
|
22359
|
-
|
|
22360
|
-
|
|
22361
|
-
|
|
22362
|
-
|
|
22363
|
-
|
|
22364
|
-
|
|
22373
|
+
// src/lib/ansi.ts
|
|
22374
|
+
var ANSI_CODES = {
|
|
22375
|
+
yellow: "\x1B[33m",
|
|
22376
|
+
magenta: "\x1B[35m",
|
|
22377
|
+
blue: "\x1B[34m",
|
|
22378
|
+
cyan: "\x1B[36m",
|
|
22379
|
+
green: "\x1B[32m",
|
|
22380
|
+
red: "\x1B[31m",
|
|
22381
|
+
// 256-color approximation of the orange trigger marker (xterm 208).
|
|
22382
|
+
orange: "\x1B[38;5;208m",
|
|
22383
|
+
gray: "\x1B[90m"
|
|
22384
|
+
};
|
|
22385
|
+
var ANSI_RESET = "\x1B[0m";
|
|
22386
|
+
var ANSI_DIM = "\x1B[2m";
|
|
22387
|
+
var ANSI_BOLD = "\x1B[1m";
|
|
22388
|
+
function supportsColor2() {
|
|
22389
|
+
if (process.env.NO_COLOR !== void 0 && process.env.NO_COLOR !== "") {
|
|
22390
|
+
return false;
|
|
22365
22391
|
}
|
|
22366
|
-
|
|
22367
|
-
|
|
22368
|
-
|
|
22369
|
-
|
|
22370
|
-
|
|
22371
|
-
if (active) {
|
|
22372
|
-
postCommitHook = ensureEffectivePostCommitHook(root).path;
|
|
22373
|
-
try {
|
|
22374
|
-
postRewriteHook = ensureEffectivePostRewriteHook(root).path;
|
|
22375
|
-
} catch (error) {
|
|
22376
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
22377
|
-
process.stderr.write(`[prim] post-rewrite hook coverage is degraded: ${detail}
|
|
22378
|
-
`);
|
|
22379
|
-
}
|
|
22380
|
-
phase = "repository binding";
|
|
22381
|
-
binding = await bindRepository(root);
|
|
22382
|
-
phase = "local activation";
|
|
22383
|
-
}
|
|
22384
|
-
setRepoActive(root, active);
|
|
22385
|
-
await daemonRequest("statusline_invalidate", {}, { timeoutMs: 250 });
|
|
22386
|
-
if (binding?.status === "unbound") {
|
|
22387
|
-
process.stderr.write(`[prim] Prim is enabled locally in ${root}
|
|
22388
|
-
`);
|
|
22389
|
-
process.stderr.write(
|
|
22390
|
-
"[prim] repository is not connected to Primitive; Moves still ingest into the team graph without repository-specific file attribution, Conflict Gate verification, or commit correlation\n"
|
|
22391
|
-
);
|
|
22392
|
-
process.stderr.write(
|
|
22393
|
-
`[prim] ask an organization owner or administrator to grant Primitive's GitHub App access to this repository through Primitive's GitHub App onboarding; binding retries automatically at the next agent SessionStart
|
|
22394
|
-
`
|
|
22395
|
-
);
|
|
22396
|
-
} else {
|
|
22397
|
-
process.stderr.write(`[prim] prim ${active ? "enabled" : "disabled"} in ${root}
|
|
22398
|
-
`);
|
|
22399
|
-
}
|
|
22400
|
-
printJson({
|
|
22401
|
-
active,
|
|
22402
|
-
repo: root,
|
|
22403
|
-
...binding ? {
|
|
22404
|
-
bindingStatus: binding.status,
|
|
22405
|
-
repositoryFullName: binding.repositoryFullName,
|
|
22406
|
-
...binding.status === "connected" ? { repoSyncId: binding.repoSyncId } : {}
|
|
22407
|
-
} : {},
|
|
22408
|
-
...postCommitHook ? { postCommitHook } : {},
|
|
22409
|
-
...postRewriteHook ? { postRewriteHook } : {}
|
|
22410
|
-
});
|
|
22411
|
-
} catch (err) {
|
|
22412
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
22413
|
-
process.stderr.write(
|
|
22414
|
-
active ? `[prim] failed to enable prim during ${phase}: ${detail}
|
|
22415
|
-
` : `[prim] failed to disable prim: ${detail}
|
|
22416
|
-
`
|
|
22417
|
-
);
|
|
22418
|
-
process.exit(1);
|
|
22392
|
+
return process.stderr.isTTY === true;
|
|
22393
|
+
}
|
|
22394
|
+
function color(text, c) {
|
|
22395
|
+
if (!supportsColor2()) {
|
|
22396
|
+
return text;
|
|
22419
22397
|
}
|
|
22398
|
+
return `${ANSI_CODES[c]}${text}${ANSI_RESET}`;
|
|
22420
22399
|
}
|
|
22421
|
-
function
|
|
22422
|
-
|
|
22423
|
-
|
|
22400
|
+
function dim(text) {
|
|
22401
|
+
if (!supportsColor2()) {
|
|
22402
|
+
return text;
|
|
22403
|
+
}
|
|
22404
|
+
return `${ANSI_DIM}${text}${ANSI_RESET}`;
|
|
22405
|
+
}
|
|
22406
|
+
function bold(text) {
|
|
22407
|
+
if (!supportsColor2()) {
|
|
22408
|
+
return text;
|
|
22409
|
+
}
|
|
22410
|
+
return `${ANSI_BOLD}${text}${ANSI_RESET}`;
|
|
22411
|
+
}
|
|
22412
|
+
var AREA_COLORS = {
|
|
22413
|
+
auth: "yellow",
|
|
22414
|
+
data: "magenta",
|
|
22415
|
+
mobile: "blue",
|
|
22416
|
+
infra: "cyan",
|
|
22417
|
+
ui: "green",
|
|
22418
|
+
billing: "orange",
|
|
22419
|
+
api: "blue",
|
|
22420
|
+
docs: "gray",
|
|
22421
|
+
testing: "gray"
|
|
22422
|
+
};
|
|
22423
|
+
function colorForArea(area) {
|
|
22424
|
+
if (!area) {
|
|
22425
|
+
return "gray";
|
|
22426
|
+
}
|
|
22427
|
+
return AREA_COLORS[area] ?? "gray";
|
|
22428
|
+
}
|
|
22429
|
+
var MAX_HEALTH_ERROR_LENGTH = 240;
|
|
22430
|
+
function boundedHealthError(value) {
|
|
22431
|
+
if (!value) return void 0;
|
|
22432
|
+
const clean = terminalSafeLine(value);
|
|
22433
|
+
if (!clean) return void 0;
|
|
22434
|
+
return clean.length <= MAX_HEALTH_ERROR_LENGTH ? clean : `${clean.slice(0, MAX_HEALTH_ERROR_LENGTH - 1)}\u2026`;
|
|
22435
|
+
}
|
|
22436
|
+
function stripAnsi2(text) {
|
|
22437
|
+
return text.replace(/\u001b\[[0-9;]*m/g, "");
|
|
22424
22438
|
}
|
|
22425
|
-
|
|
22426
|
-
// src/commands/auth.ts
|
|
22427
|
-
import { execFile } from "child_process";
|
|
22428
|
-
import { createHash as createHash6, randomBytes as randomBytes3 } from "crypto";
|
|
22429
|
-
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
|
|
22430
|
-
import { createServer } from "http";
|
|
22431
|
-
import { platform } from "os";
|
|
22432
|
-
|
|
22433
|
-
// src/auth/api-keys.ts
|
|
22434
|
-
import { randomBytes as randomBytes2 } from "crypto";
|
|
22435
22439
|
|
|
22436
22440
|
// src/generated/cli-http-v1.validators.js
|
|
22437
22441
|
var import_ucs2length = __toESM(require_ucs2length(), 1);
|
|
@@ -32165,7 +32169,114 @@ function isGitHubInstallIntentStatusResponse(value) {
|
|
|
32165
32169
|
}
|
|
32166
32170
|
}
|
|
32167
32171
|
|
|
32172
|
+
// src/lib/github-install-intent.ts
|
|
32173
|
+
var PROTOCOL_VERSION = 1;
|
|
32174
|
+
var MODE = "install_intent_v1";
|
|
32175
|
+
var START_PATH = "/api/cli/github/install-intents";
|
|
32176
|
+
var POLL_REQUEST_TIMEOUT_MS2 = 1e4;
|
|
32177
|
+
var MAX_INTENT_LIFETIME_MS = 15 * 6e4;
|
|
32178
|
+
var MAX_SERVER_CLOCK_AHEAD_MS = 3e4;
|
|
32179
|
+
var REPOSITORY_IDENTITY_MIGRATION_REQUIRED = "repository_identity_migration_required";
|
|
32180
|
+
function isRepositoryIdentityMigrationRequiredStatus(value) {
|
|
32181
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
32182
|
+
return false;
|
|
32183
|
+
}
|
|
32184
|
+
const record2 = value;
|
|
32185
|
+
if (record2.failureCode !== REPOSITORY_IDENTITY_MIGRATION_REQUIRED) {
|
|
32186
|
+
return false;
|
|
32187
|
+
}
|
|
32188
|
+
return isGitHubInstallIntentStatusResponse({
|
|
32189
|
+
...record2,
|
|
32190
|
+
failureCode: "proof_commit_failed"
|
|
32191
|
+
});
|
|
32192
|
+
}
|
|
32193
|
+
function parseGitHubInstallIntentStart(value, now = Date.now()) {
|
|
32194
|
+
if (!isGitHubInstallIntentStartResponse(value) || value.expiresAt <= now || value.expiresAt - now > MAX_INTENT_LIFETIME_MS + MAX_SERVER_CLOCK_AHEAD_MS) {
|
|
32195
|
+
return null;
|
|
32196
|
+
}
|
|
32197
|
+
return value;
|
|
32198
|
+
}
|
|
32199
|
+
function parseGitHubInstallIntentStatus(value, expectedExpiresAt) {
|
|
32200
|
+
if (isGitHubInstallIntentStatusResponse(value)) {
|
|
32201
|
+
return value.expiresAt === expectedExpiresAt ? value : null;
|
|
32202
|
+
}
|
|
32203
|
+
if (!isRepositoryIdentityMigrationRequiredStatus(value) || value.expiresAt !== expectedExpiresAt) {
|
|
32204
|
+
return null;
|
|
32205
|
+
}
|
|
32206
|
+
return value;
|
|
32207
|
+
}
|
|
32208
|
+
async function createGitHubInstallIntent(client, options = {}) {
|
|
32209
|
+
const { now = Date.now, ...requestOptions } = options;
|
|
32210
|
+
const raw = await client.post(START_PATH, void 0, requestOptions);
|
|
32211
|
+
const parsed = parseGitHubInstallIntentStart(raw, now());
|
|
32212
|
+
if (!parsed) throw new Error("server returned an invalid GitHub install-intent response");
|
|
32213
|
+
return parsed;
|
|
32214
|
+
}
|
|
32215
|
+
function defaultSleep3(milliseconds, signal) {
|
|
32216
|
+
if (signal.aborted) return Promise.reject(signal.reason);
|
|
32217
|
+
return new Promise((resolvePromise, reject) => {
|
|
32218
|
+
const onAbort = () => {
|
|
32219
|
+
clearTimeout(timer);
|
|
32220
|
+
reject(signal.reason);
|
|
32221
|
+
};
|
|
32222
|
+
const timer = setTimeout(() => {
|
|
32223
|
+
signal.removeEventListener("abort", onAbort);
|
|
32224
|
+
resolvePromise();
|
|
32225
|
+
}, milliseconds);
|
|
32226
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
32227
|
+
});
|
|
32228
|
+
}
|
|
32229
|
+
function expiredStatus(start) {
|
|
32230
|
+
return {
|
|
32231
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
32232
|
+
mode: MODE,
|
|
32233
|
+
found: true,
|
|
32234
|
+
status: "expired",
|
|
32235
|
+
expiresAt: start.expiresAt,
|
|
32236
|
+
closedAt: start.expiresAt
|
|
32237
|
+
};
|
|
32238
|
+
}
|
|
32239
|
+
async function pollGitHubInstallIntent(client, start, options = {}) {
|
|
32240
|
+
const now = options.now ?? Date.now;
|
|
32241
|
+
const sleep2 = options.sleep ?? defaultSleep3;
|
|
32242
|
+
const signal = options.signal ?? new AbortController().signal;
|
|
32243
|
+
const path9 = `${START_PATH}/${encodeURIComponent(start.intentId)}`;
|
|
32244
|
+
while (true) {
|
|
32245
|
+
if (signal.aborted) throw signal.reason;
|
|
32246
|
+
const remainingBeforeSleep = start.expiresAt - now();
|
|
32247
|
+
if (remainingBeforeSleep <= 0) return expiredStatus(start);
|
|
32248
|
+
await sleep2(Math.min(start.pollAfterMs, remainingBeforeSleep), signal);
|
|
32249
|
+
if (signal.aborted) throw signal.reason;
|
|
32250
|
+
const remainingBeforeRequest = start.expiresAt - now();
|
|
32251
|
+
if (remainingBeforeRequest <= 0) return expiredStatus(start);
|
|
32252
|
+
let raw;
|
|
32253
|
+
try {
|
|
32254
|
+
raw = await client.get(path9, {
|
|
32255
|
+
signal: AbortSignal.any([
|
|
32256
|
+
signal,
|
|
32257
|
+
AbortSignal.timeout(Math.min(POLL_REQUEST_TIMEOUT_MS2, remainingBeforeRequest))
|
|
32258
|
+
])
|
|
32259
|
+
});
|
|
32260
|
+
} catch (error) {
|
|
32261
|
+
if (signal.aborted) throw error;
|
|
32262
|
+
if (now() >= start.expiresAt) return expiredStatus(start);
|
|
32263
|
+
throw error;
|
|
32264
|
+
}
|
|
32265
|
+
const status = parseGitHubInstallIntentStatus(raw, start.expiresAt);
|
|
32266
|
+
if (!status) throw new Error("server returned an invalid GitHub install-intent status");
|
|
32267
|
+
if (status.status !== "pending" && status.status !== "claimed") return status;
|
|
32268
|
+
}
|
|
32269
|
+
}
|
|
32270
|
+
|
|
32271
|
+
// src/commands/auth.ts
|
|
32272
|
+
import { execFile } from "child_process";
|
|
32273
|
+
import { createHash as createHash6, randomBytes as randomBytes3 } from "crypto";
|
|
32274
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
|
|
32275
|
+
import { createServer } from "http";
|
|
32276
|
+
import { platform } from "os";
|
|
32277
|
+
|
|
32168
32278
|
// src/auth/api-keys.ts
|
|
32279
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
32169
32280
|
var USER_API_KEY_TIMEOUT_MS = 1e4;
|
|
32170
32281
|
var USER_API_KEY_EXIT = {
|
|
32171
32282
|
ok: 0,
|
|
@@ -33054,7 +33165,232 @@ async function exchangeCode(siteUrl, code, codeVerifier, redirectUri) {
|
|
|
33054
33165
|
};
|
|
33055
33166
|
}
|
|
33056
33167
|
|
|
33057
|
-
// src/commands/
|
|
33168
|
+
// src/commands/github.ts
|
|
33169
|
+
var BIND_TIMEOUT_MS = 1e4;
|
|
33170
|
+
var START_TIMEOUT_MS = 1e4;
|
|
33171
|
+
var EXIT_OK2 = 0;
|
|
33172
|
+
var EXIT_FAILURE = 1;
|
|
33173
|
+
var EXIT_UNBOUND = 2;
|
|
33174
|
+
var defaultDependencies2 = {
|
|
33175
|
+
cwd: () => process.cwd(),
|
|
33176
|
+
gitToplevel,
|
|
33177
|
+
bindRepositoryWithClient,
|
|
33178
|
+
getPinnedClient,
|
|
33179
|
+
createInstallIntent: createGitHubInstallIntent,
|
|
33180
|
+
pollInstallIntent: pollGitHubInstallIntent,
|
|
33181
|
+
openBrowser,
|
|
33182
|
+
now: Date.now
|
|
33183
|
+
};
|
|
33184
|
+
function errorMessage(error) {
|
|
33185
|
+
return error instanceof Error ? error.message : String(error);
|
|
33186
|
+
}
|
|
33187
|
+
function reportConnectFailure(error) {
|
|
33188
|
+
const message = errorMessage(error);
|
|
33189
|
+
const human = boundedHealthError(message) ?? "unknown error";
|
|
33190
|
+
process.stderr.write(`[prim] github connect failed: ${human}
|
|
33191
|
+
`);
|
|
33192
|
+
printJson({
|
|
33193
|
+
status: "error",
|
|
33194
|
+
error: message,
|
|
33195
|
+
...error instanceof HttpError ? { httpStatus: error.status } : {}
|
|
33196
|
+
});
|
|
33197
|
+
process.exitCode = EXIT_FAILURE;
|
|
33198
|
+
}
|
|
33199
|
+
function reportBinding(binding, installAttempted = false) {
|
|
33200
|
+
if (binding.status === "connected") {
|
|
33201
|
+
process.stderr.write(
|
|
33202
|
+
`[prim] repository binding connected for GitHub origin ${binding.repositoryFullName}
|
|
33203
|
+
`
|
|
33204
|
+
);
|
|
33205
|
+
printJson(binding);
|
|
33206
|
+
process.exitCode = EXIT_OK2;
|
|
33207
|
+
return;
|
|
33208
|
+
}
|
|
33209
|
+
process.stderr.write(
|
|
33210
|
+
installAttempted ? `[prim] GitHub installation completed, but ${binding.repositoryFullName} was not granted admin access; repository binding remains unbound
|
|
33211
|
+
` : `[prim] repository binding unbound for GitHub origin ${binding.repositoryFullName}; starting GitHub App installation
|
|
33212
|
+
`
|
|
33213
|
+
);
|
|
33214
|
+
printJson(binding);
|
|
33215
|
+
process.exitCode = EXIT_UNBOUND;
|
|
33216
|
+
}
|
|
33217
|
+
function terminalInstallError(status) {
|
|
33218
|
+
if (status.status === "failed_terminal") {
|
|
33219
|
+
return new Error(`GitHub installation failed: ${status.failureCode}`);
|
|
33220
|
+
}
|
|
33221
|
+
return new Error(`GitHub installation ${status.status}`);
|
|
33222
|
+
}
|
|
33223
|
+
async function completeInstallIntent(client, start, dependencies, browser) {
|
|
33224
|
+
process.stderr.write(`[prim] complete GitHub App installation at:
|
|
33225
|
+
${start.browserUrl}
|
|
33226
|
+
`);
|
|
33227
|
+
if (browser) dependencies.openBrowser(start.browserUrl);
|
|
33228
|
+
return dependencies.pollInstallIntent(client, start, {
|
|
33229
|
+
now: dependencies.now
|
|
33230
|
+
});
|
|
33231
|
+
}
|
|
33232
|
+
async function runGithubConnect(dependencies = defaultDependencies2, options = {}) {
|
|
33233
|
+
const root = options.root ?? dependencies.gitToplevel(dependencies.cwd());
|
|
33234
|
+
if (!root) return { kind: "not-a-repo" };
|
|
33235
|
+
try {
|
|
33236
|
+
const client = await dependencies.getPinnedClient({
|
|
33237
|
+
signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
|
|
33238
|
+
});
|
|
33239
|
+
const existing = await dependencies.bindRepositoryWithClient(root, client, {
|
|
33240
|
+
signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
|
|
33241
|
+
});
|
|
33242
|
+
if (existing.status === "connected") {
|
|
33243
|
+
return { kind: "connected", binding: existing };
|
|
33244
|
+
}
|
|
33245
|
+
const start = await dependencies.createInstallIntent(client, {
|
|
33246
|
+
signal: AbortSignal.timeout(START_TIMEOUT_MS),
|
|
33247
|
+
now: dependencies.now
|
|
33248
|
+
});
|
|
33249
|
+
const status = await completeInstallIntent(
|
|
33250
|
+
client,
|
|
33251
|
+
start,
|
|
33252
|
+
dependencies,
|
|
33253
|
+
options.browser ?? true
|
|
33254
|
+
);
|
|
33255
|
+
if (status.status !== "consumed") throw terminalInstallError(status);
|
|
33256
|
+
process.stderr.write(
|
|
33257
|
+
`[prim] GitHub installation verified: ${status.adminRepositoryCount} admin repositories (${status.repositoryCount} total)
|
|
33258
|
+
`
|
|
33259
|
+
);
|
|
33260
|
+
const binding = await dependencies.bindRepositoryWithClient(root, client, {
|
|
33261
|
+
signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
|
|
33262
|
+
});
|
|
33263
|
+
return binding.status === "connected" ? { kind: "connected", binding } : { kind: "unbound", binding, installAttempted: true };
|
|
33264
|
+
} catch (error) {
|
|
33265
|
+
return { kind: "error", error };
|
|
33266
|
+
}
|
|
33267
|
+
}
|
|
33268
|
+
async function performGithubConnect(dependencies = defaultDependencies2, options = {}) {
|
|
33269
|
+
const outcome = await runGithubConnect(dependencies, options);
|
|
33270
|
+
switch (outcome.kind) {
|
|
33271
|
+
case "not-a-repo":
|
|
33272
|
+
reportConnectFailure(
|
|
33273
|
+
new Error("not a git repository \u2014 run `prim github connect` inside a GitHub repository")
|
|
33274
|
+
);
|
|
33275
|
+
return;
|
|
33276
|
+
case "error":
|
|
33277
|
+
reportConnectFailure(outcome.error);
|
|
33278
|
+
return;
|
|
33279
|
+
case "connected":
|
|
33280
|
+
reportBinding(outcome.binding);
|
|
33281
|
+
return;
|
|
33282
|
+
case "unbound":
|
|
33283
|
+
reportBinding(outcome.binding, outcome.installAttempted);
|
|
33284
|
+
return;
|
|
33285
|
+
}
|
|
33286
|
+
}
|
|
33287
|
+
function registerGithubCommands(program3, dependencies = defaultDependencies2) {
|
|
33288
|
+
const github = program3.command("github").description("Manage GitHub repository bindings");
|
|
33289
|
+
github.command("connect").description("Install or reuse the Primitive GitHub App and bind the current GitHub origin").option("--no-browser", "Print the GitHub installation URL without opening a browser").action(async (options, command) => {
|
|
33290
|
+
const globals = command.optsWithGlobals();
|
|
33291
|
+
await performGithubConnect(dependencies, {
|
|
33292
|
+
browser: options.browser && globals.nonInteractive !== true
|
|
33293
|
+
});
|
|
33294
|
+
});
|
|
33295
|
+
}
|
|
33296
|
+
|
|
33297
|
+
// src/commands/activation.ts
|
|
33298
|
+
var CONNECT_PROMPT = "[prim] Connect this repository to Primitive via the GitHub App now?";
|
|
33299
|
+
async function maybeConnectRepository(root, globals) {
|
|
33300
|
+
if (isNonInteractive(globals)) return void 0;
|
|
33301
|
+
const approved = Boolean(globals.yes) || await askConfirmation(CONNECT_PROMPT, process.stderr);
|
|
33302
|
+
if (!approved) return void 0;
|
|
33303
|
+
const outcome = await runGithubConnect(void 0, { root, browser: true });
|
|
33304
|
+
if (outcome.kind === "connected") {
|
|
33305
|
+
process.stderr.write(
|
|
33306
|
+
`[prim] repository binding connected for GitHub origin ${outcome.binding.repositoryFullName}
|
|
33307
|
+
`
|
|
33308
|
+
);
|
|
33309
|
+
return outcome.binding;
|
|
33310
|
+
}
|
|
33311
|
+
if (outcome.kind === "error") {
|
|
33312
|
+
const detail = outcome.error instanceof Error ? outcome.error.message : String(outcome.error);
|
|
33313
|
+
process.stderr.write(`[prim] connect could not complete: ${detail}
|
|
33314
|
+
`);
|
|
33315
|
+
}
|
|
33316
|
+
return void 0;
|
|
33317
|
+
}
|
|
33318
|
+
async function applyActivation(active, globals = {}) {
|
|
33319
|
+
const root = gitToplevel();
|
|
33320
|
+
if (!root) {
|
|
33321
|
+
process.stderr.write(
|
|
33322
|
+
`[prim] not a git repository \u2014 run \`prim ${active ? "enable" : "disable"}\` inside a repo
|
|
33323
|
+
`
|
|
33324
|
+
);
|
|
33325
|
+
process.exit(1);
|
|
33326
|
+
}
|
|
33327
|
+
let phase = active ? "post-commit hook coverage" : "local deactivation";
|
|
33328
|
+
try {
|
|
33329
|
+
let binding;
|
|
33330
|
+
let postCommitHook;
|
|
33331
|
+
let postRewriteHook;
|
|
33332
|
+
if (active) {
|
|
33333
|
+
postCommitHook = ensureEffectivePostCommitHook(root).path;
|
|
33334
|
+
try {
|
|
33335
|
+
postRewriteHook = ensureEffectivePostRewriteHook(root).path;
|
|
33336
|
+
} catch (error) {
|
|
33337
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
33338
|
+
process.stderr.write(`[prim] post-rewrite hook coverage is degraded: ${detail}
|
|
33339
|
+
`);
|
|
33340
|
+
}
|
|
33341
|
+
phase = "repository binding";
|
|
33342
|
+
binding = await bindRepository(root);
|
|
33343
|
+
phase = "local activation";
|
|
33344
|
+
}
|
|
33345
|
+
setRepoActive(root, active);
|
|
33346
|
+
if (active && binding?.status === "unbound") {
|
|
33347
|
+
const connected = await maybeConnectRepository(root, globals);
|
|
33348
|
+
if (connected) binding = connected;
|
|
33349
|
+
}
|
|
33350
|
+
await daemonRequest("statusline_invalidate", {}, { timeoutMs: 250 });
|
|
33351
|
+
if (binding?.status === "unbound") {
|
|
33352
|
+
process.stderr.write(`[prim] Prim is enabled locally in ${root}
|
|
33353
|
+
`);
|
|
33354
|
+
process.stderr.write(
|
|
33355
|
+
"[prim] repository is not connected to Primitive; Moves still ingest into the team graph without repository-specific file attribution, Conflict Gate verification, or commit correlation\n"
|
|
33356
|
+
);
|
|
33357
|
+
process.stderr.write(
|
|
33358
|
+
`[prim] ask an organization owner or administrator to grant Primitive's GitHub App access to this repository through Primitive's GitHub App onboarding; binding retries automatically at the next agent SessionStart
|
|
33359
|
+
`
|
|
33360
|
+
);
|
|
33361
|
+
} else {
|
|
33362
|
+
process.stderr.write(`[prim] prim ${active ? "enabled" : "disabled"} in ${root}
|
|
33363
|
+
`);
|
|
33364
|
+
}
|
|
33365
|
+
printJson({
|
|
33366
|
+
active,
|
|
33367
|
+
repo: root,
|
|
33368
|
+
...binding ? {
|
|
33369
|
+
bindingStatus: binding.status,
|
|
33370
|
+
repositoryFullName: binding.repositoryFullName,
|
|
33371
|
+
...binding.status === "connected" ? { repoSyncId: binding.repoSyncId } : {}
|
|
33372
|
+
} : {},
|
|
33373
|
+
...postCommitHook ? { postCommitHook } : {},
|
|
33374
|
+
...postRewriteHook ? { postRewriteHook } : {}
|
|
33375
|
+
});
|
|
33376
|
+
} catch (err) {
|
|
33377
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
33378
|
+
process.stderr.write(
|
|
33379
|
+
active ? `[prim] failed to enable prim during ${phase}: ${detail}
|
|
33380
|
+
` : `[prim] failed to disable prim: ${detail}
|
|
33381
|
+
`
|
|
33382
|
+
);
|
|
33383
|
+
process.exit(1);
|
|
33384
|
+
}
|
|
33385
|
+
}
|
|
33386
|
+
function registerActivationCommands(program3) {
|
|
33387
|
+
program3.command("enable").description("Activate prim's hooks in this repo (git config prim.active=true)").action((_opts, command) => applyActivation(true, command.optsWithGlobals()));
|
|
33388
|
+
program3.command("disable").description("Mute prim's hooks in this repo (git config prim.active=false)").action(
|
|
33389
|
+
(_opts, command) => applyActivation(false, command.optsWithGlobals())
|
|
33390
|
+
);
|
|
33391
|
+
}
|
|
33392
|
+
|
|
33393
|
+
// src/commands/claude-install.ts
|
|
33058
33394
|
import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
|
|
33059
33395
|
import { homedir as homedir4 } from "os";
|
|
33060
33396
|
import { join as join10 } from "path";
|
|
@@ -34855,7 +35191,7 @@ function atomicWrite3(path9, content, mode, validate3) {
|
|
|
34855
35191
|
}
|
|
34856
35192
|
return true;
|
|
34857
35193
|
}
|
|
34858
|
-
function
|
|
35194
|
+
function defaultSleep4(ms) {
|
|
34859
35195
|
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
34860
35196
|
}
|
|
34861
35197
|
function validatePlist(path9, timeoutMs2) {
|
|
@@ -35174,7 +35510,7 @@ async function ensureMacDaemonLocked(options) {
|
|
|
35174
35510
|
PLIST_FILE_MODE,
|
|
35175
35511
|
options.validatePlist ?? validatePlist
|
|
35176
35512
|
);
|
|
35177
|
-
const sleep2 = options.sleep ??
|
|
35513
|
+
const sleep2 = options.sleep ?? defaultSleep4;
|
|
35178
35514
|
const nowMs = options.nowMs ?? performance.now.bind(performance);
|
|
35179
35515
|
const deadlineMs = nowMs() + TRANSITION_TIMEOUT_MS;
|
|
35180
35516
|
const inspect = options.inspectDaemon ?? inspectDaemon;
|
|
@@ -35228,7 +35564,7 @@ async function bootoutMacDaemon(options = {}) {
|
|
|
35228
35564
|
async function bootoutMacDaemonLocked(options) {
|
|
35229
35565
|
const runner = options.runner ?? runLaunchctl;
|
|
35230
35566
|
const inspect = options.inspectDaemon ?? inspectDaemon;
|
|
35231
|
-
const sleep2 = options.sleep ??
|
|
35567
|
+
const sleep2 = options.sleep ?? defaultSleep4;
|
|
35232
35568
|
const nowMs = options.nowMs ?? performance.now.bind(performance);
|
|
35233
35569
|
const deadlineMs = nowMs() + TRANSITION_TIMEOUT_MS;
|
|
35234
35570
|
const paths = launchdPaths(options);
|
|
@@ -35282,73 +35618,6 @@ async function bootoutMacDaemonLocked(options) {
|
|
|
35282
35618
|
return { wasLoaded, legacyStopped, service };
|
|
35283
35619
|
}
|
|
35284
35620
|
|
|
35285
|
-
// src/lib/ansi.ts
|
|
35286
|
-
var ANSI_CODES = {
|
|
35287
|
-
yellow: "\x1B[33m",
|
|
35288
|
-
magenta: "\x1B[35m",
|
|
35289
|
-
blue: "\x1B[34m",
|
|
35290
|
-
cyan: "\x1B[36m",
|
|
35291
|
-
green: "\x1B[32m",
|
|
35292
|
-
red: "\x1B[31m",
|
|
35293
|
-
// 256-color approximation of the orange trigger marker (xterm 208).
|
|
35294
|
-
orange: "\x1B[38;5;208m",
|
|
35295
|
-
gray: "\x1B[90m"
|
|
35296
|
-
};
|
|
35297
|
-
var ANSI_RESET = "\x1B[0m";
|
|
35298
|
-
var ANSI_DIM = "\x1B[2m";
|
|
35299
|
-
var ANSI_BOLD = "\x1B[1m";
|
|
35300
|
-
function supportsColor2() {
|
|
35301
|
-
if (process.env.NO_COLOR !== void 0 && process.env.NO_COLOR !== "") {
|
|
35302
|
-
return false;
|
|
35303
|
-
}
|
|
35304
|
-
return process.stderr.isTTY === true;
|
|
35305
|
-
}
|
|
35306
|
-
function color(text, c) {
|
|
35307
|
-
if (!supportsColor2()) {
|
|
35308
|
-
return text;
|
|
35309
|
-
}
|
|
35310
|
-
return `${ANSI_CODES[c]}${text}${ANSI_RESET}`;
|
|
35311
|
-
}
|
|
35312
|
-
function dim(text) {
|
|
35313
|
-
if (!supportsColor2()) {
|
|
35314
|
-
return text;
|
|
35315
|
-
}
|
|
35316
|
-
return `${ANSI_DIM}${text}${ANSI_RESET}`;
|
|
35317
|
-
}
|
|
35318
|
-
function bold(text) {
|
|
35319
|
-
if (!supportsColor2()) {
|
|
35320
|
-
return text;
|
|
35321
|
-
}
|
|
35322
|
-
return `${ANSI_BOLD}${text}${ANSI_RESET}`;
|
|
35323
|
-
}
|
|
35324
|
-
var AREA_COLORS = {
|
|
35325
|
-
auth: "yellow",
|
|
35326
|
-
data: "magenta",
|
|
35327
|
-
mobile: "blue",
|
|
35328
|
-
infra: "cyan",
|
|
35329
|
-
ui: "green",
|
|
35330
|
-
billing: "orange",
|
|
35331
|
-
api: "blue",
|
|
35332
|
-
docs: "gray",
|
|
35333
|
-
testing: "gray"
|
|
35334
|
-
};
|
|
35335
|
-
function colorForArea(area) {
|
|
35336
|
-
if (!area) {
|
|
35337
|
-
return "gray";
|
|
35338
|
-
}
|
|
35339
|
-
return AREA_COLORS[area] ?? "gray";
|
|
35340
|
-
}
|
|
35341
|
-
var MAX_HEALTH_ERROR_LENGTH = 240;
|
|
35342
|
-
function boundedHealthError(value) {
|
|
35343
|
-
if (!value) return void 0;
|
|
35344
|
-
const clean = terminalSafeLine(value);
|
|
35345
|
-
if (!clean) return void 0;
|
|
35346
|
-
return clean.length <= MAX_HEALTH_ERROR_LENGTH ? clean : `${clean.slice(0, MAX_HEALTH_ERROR_LENGTH - 1)}\u2026`;
|
|
35347
|
-
}
|
|
35348
|
-
function stripAnsi2(text) {
|
|
35349
|
-
return text.replace(/\u001b\[[0-9;]*m/g, "");
|
|
35350
|
-
}
|
|
35351
|
-
|
|
35352
35621
|
// src/lib/presence.ts
|
|
35353
35622
|
var DECISION_ORIGIN = "https://app.getprimitive.ai";
|
|
35354
35623
|
function validDecisionUrl(value) {
|
|
@@ -35468,7 +35737,7 @@ var READY_POLL_MS2 = 100;
|
|
|
35468
35737
|
var READY_PROBE_TIMEOUT_MS2 = 250;
|
|
35469
35738
|
var HEALTHY_TIMEOUT_MS = 3e4;
|
|
35470
35739
|
var HEALTHY_POLL_MS = 250;
|
|
35471
|
-
var
|
|
35740
|
+
var EXIT_OK3 = 0;
|
|
35472
35741
|
var EXIT_NOT_RUNNING = 2;
|
|
35473
35742
|
var EXIT_BOOTING = 3;
|
|
35474
35743
|
function formatDaemonLifecycleMessage(message, decisionIngestion) {
|
|
@@ -35849,7 +36118,7 @@ function classifyStatus(pidAlive, responding, snapshot, pid) {
|
|
|
35849
36118
|
};
|
|
35850
36119
|
}
|
|
35851
36120
|
if (!snapshot) {
|
|
35852
|
-
return { json: { running: true, responding: true }, exitCode:
|
|
36121
|
+
return { json: { running: true, responding: true }, exitCode: EXIT_OK3 };
|
|
35853
36122
|
}
|
|
35854
36123
|
if (snapshot.healthy === false) {
|
|
35855
36124
|
return {
|
|
@@ -35857,7 +36126,7 @@ function classifyStatus(pidAlive, responding, snapshot, pid) {
|
|
|
35857
36126
|
exitCode: EXIT_BOOTING
|
|
35858
36127
|
};
|
|
35859
36128
|
}
|
|
35860
|
-
return { json: { running: true, responding: true, ...snapshot }, exitCode:
|
|
36129
|
+
return { json: { running: true, responding: true, ...snapshot }, exitCode: EXIT_OK3 };
|
|
35861
36130
|
}
|
|
35862
36131
|
function classifyLaunchdStatus(service, responding, snapshot, disabled) {
|
|
35863
36132
|
if (!service) {
|
|
@@ -35955,7 +36224,7 @@ function classifyLaunchdStatus(service, responding, snapshot, disabled) {
|
|
|
35955
36224
|
disabled,
|
|
35956
36225
|
...snapshot
|
|
35957
36226
|
},
|
|
35958
|
-
exitCode:
|
|
36227
|
+
exitCode: EXIT_OK3
|
|
35959
36228
|
};
|
|
35960
36229
|
}
|
|
35961
36230
|
function formatDaemonSnapshotMessage(snapshot, supervised, decisionIngestion, bindingState) {
|
|
@@ -36005,7 +36274,7 @@ async function detachedDaemonStatus() {
|
|
|
36005
36274
|
writeLiveSnapshot(snapshot);
|
|
36006
36275
|
}
|
|
36007
36276
|
console.log(JSON.stringify(json, null, 2));
|
|
36008
|
-
if (exitCode !==
|
|
36277
|
+
if (exitCode !== EXIT_OK3 && !process.exitCode) {
|
|
36009
36278
|
process.exitCode = exitCode;
|
|
36010
36279
|
}
|
|
36011
36280
|
}
|
|
@@ -36055,7 +36324,7 @@ async function macDaemonStatus() {
|
|
|
36055
36324
|
writeLiveSnapshot(snapshot, true);
|
|
36056
36325
|
}
|
|
36057
36326
|
console.log(JSON.stringify(json, null, 2));
|
|
36058
|
-
if (exitCode !==
|
|
36327
|
+
if (exitCode !== EXIT_OK3 && !process.exitCode) process.exitCode = exitCode;
|
|
36059
36328
|
}
|
|
36060
36329
|
async function daemonStatus() {
|
|
36061
36330
|
if (process.platform === "darwin") {
|
|
@@ -36672,7 +36941,7 @@ var DECISION_LIFECYCLE_EXIT = {
|
|
|
36672
36941
|
server: 3,
|
|
36673
36942
|
notFound: 4
|
|
36674
36943
|
};
|
|
36675
|
-
var
|
|
36944
|
+
var defaultDependencies3 = {
|
|
36676
36945
|
getClient,
|
|
36677
36946
|
signal: () => AbortSignal.timeout(DECISION_LIFECYCLE_TIMEOUT_MS),
|
|
36678
36947
|
writeStdout: (value) => console.log(value),
|
|
@@ -36885,19 +37154,19 @@ async function executeLifecycle(operation, request2, dependencies) {
|
|
|
36885
37154
|
return writeFailure2(operation, classifyError2(error), dependencies);
|
|
36886
37155
|
}
|
|
36887
37156
|
}
|
|
36888
|
-
function publishDecision(id, dependencies =
|
|
37157
|
+
function publishDecision(id, dependencies = defaultDependencies3) {
|
|
36889
37158
|
const request2 = { id };
|
|
36890
37159
|
return executeLifecycle("publish", request2, dependencies);
|
|
36891
37160
|
}
|
|
36892
|
-
function restoreDecision(id, dependencies =
|
|
37161
|
+
function restoreDecision(id, dependencies = defaultDependencies3) {
|
|
36893
37162
|
const request2 = { id };
|
|
36894
37163
|
return executeLifecycle("restore", request2, dependencies);
|
|
36895
37164
|
}
|
|
36896
|
-
function ratifyDecision(id, dependencies =
|
|
37165
|
+
function ratifyDecision(id, dependencies = defaultDependencies3) {
|
|
36897
37166
|
const request2 = { id };
|
|
36898
37167
|
return executeLifecycle("ratify", request2, dependencies);
|
|
36899
37168
|
}
|
|
36900
|
-
function supersedeDecision(id, by, dependencies =
|
|
37169
|
+
function supersedeDecision(id, by, dependencies = defaultDependencies3) {
|
|
36901
37170
|
const request2 = { id, by };
|
|
36902
37171
|
return executeLifecycle("supersede", request2, dependencies);
|
|
36903
37172
|
}
|
|
@@ -37736,26 +38005,9 @@ function formatDecisionsWarning(result) {
|
|
|
37736
38005
|
return lines.join("\n");
|
|
37737
38006
|
}
|
|
37738
38007
|
|
|
37739
|
-
// src/lib/confirmation.ts
|
|
37740
|
-
function isNonInteractive(globals) {
|
|
37741
|
-
return Boolean(globals.nonInteractive || process.env.CI || process.env.PRIM_NON_INTERACTIVE);
|
|
37742
|
-
}
|
|
37743
|
-
async function askConfirmation(question, output = process.stdout) {
|
|
37744
|
-
if (!process.stdin.isTTY) return false;
|
|
37745
|
-
const { createInterface } = await import("readline/promises");
|
|
37746
|
-
const rl = createInterface({ input: process.stdin, output });
|
|
37747
|
-
try {
|
|
37748
|
-
const answer = await rl.question(`${question} [y/N] `);
|
|
37749
|
-
const normalized = answer.trim().toLowerCase();
|
|
37750
|
-
return normalized === "y" || normalized === "yes";
|
|
37751
|
-
} finally {
|
|
37752
|
-
rl.close();
|
|
37753
|
-
}
|
|
37754
|
-
}
|
|
37755
|
-
|
|
37756
38008
|
// src/commands/decisions.ts
|
|
37757
38009
|
var EXIT_NOT_FOUND = 4;
|
|
37758
|
-
var
|
|
38010
|
+
var EXIT_FAILURE2 = 1;
|
|
37759
38011
|
var EXIT_USAGE = 2;
|
|
37760
38012
|
function handleRepairCommandError(error) {
|
|
37761
38013
|
if (error instanceof RepairProposalNotFoundError) {
|
|
@@ -37772,12 +38024,12 @@ function handleRepairCommandError(error) {
|
|
|
37772
38024
|
console.error(
|
|
37773
38025
|
`[prim] ${error instanceof RepairAuthorizationError ? error.message : "Not authorized to review commit repairs; active organization membership and current repository authorization are required"}`
|
|
37774
38026
|
);
|
|
37775
|
-
process.exitCode =
|
|
38027
|
+
process.exitCode = EXIT_FAILURE2;
|
|
37776
38028
|
return true;
|
|
37777
38029
|
}
|
|
37778
38030
|
if (error instanceof RepairEndpointVersionError || error instanceof RepairListContractError) {
|
|
37779
38031
|
console.error(`[prim] ${error.message}`);
|
|
37780
|
-
process.exitCode =
|
|
38032
|
+
process.exitCode = EXIT_FAILURE2;
|
|
37781
38033
|
return true;
|
|
37782
38034
|
}
|
|
37783
38035
|
return false;
|
|
@@ -39802,220 +40054,6 @@ function registerDoctorCommands(program3) {
|
|
|
39802
40054
|
});
|
|
39803
40055
|
}
|
|
39804
40056
|
|
|
39805
|
-
// src/lib/github-install-intent.ts
|
|
39806
|
-
var PROTOCOL_VERSION = 1;
|
|
39807
|
-
var MODE = "install_intent_v1";
|
|
39808
|
-
var START_PATH = "/api/cli/github/install-intents";
|
|
39809
|
-
var POLL_REQUEST_TIMEOUT_MS2 = 1e4;
|
|
39810
|
-
var MAX_INTENT_LIFETIME_MS = 15 * 6e4;
|
|
39811
|
-
var REPOSITORY_IDENTITY_MIGRATION_REQUIRED = "repository_identity_migration_required";
|
|
39812
|
-
function isRepositoryIdentityMigrationRequiredStatus(value) {
|
|
39813
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
39814
|
-
return false;
|
|
39815
|
-
}
|
|
39816
|
-
const record2 = value;
|
|
39817
|
-
if (record2.failureCode !== REPOSITORY_IDENTITY_MIGRATION_REQUIRED) {
|
|
39818
|
-
return false;
|
|
39819
|
-
}
|
|
39820
|
-
return isGitHubInstallIntentStatusResponse({
|
|
39821
|
-
...record2,
|
|
39822
|
-
failureCode: "proof_commit_failed"
|
|
39823
|
-
});
|
|
39824
|
-
}
|
|
39825
|
-
function parseGitHubInstallIntentStart(value, now = Date.now()) {
|
|
39826
|
-
if (!isGitHubInstallIntentStartResponse(value) || value.expiresAt <= now || value.expiresAt - now > MAX_INTENT_LIFETIME_MS) {
|
|
39827
|
-
return null;
|
|
39828
|
-
}
|
|
39829
|
-
return value;
|
|
39830
|
-
}
|
|
39831
|
-
function parseGitHubInstallIntentStatus(value, expectedExpiresAt) {
|
|
39832
|
-
if (isGitHubInstallIntentStatusResponse(value)) {
|
|
39833
|
-
return value.expiresAt === expectedExpiresAt ? value : null;
|
|
39834
|
-
}
|
|
39835
|
-
if (!isRepositoryIdentityMigrationRequiredStatus(value) || value.expiresAt !== expectedExpiresAt) {
|
|
39836
|
-
return null;
|
|
39837
|
-
}
|
|
39838
|
-
return value;
|
|
39839
|
-
}
|
|
39840
|
-
async function createGitHubInstallIntent(client, options = {}) {
|
|
39841
|
-
const { now = Date.now, ...requestOptions } = options;
|
|
39842
|
-
const raw = await client.post(START_PATH, void 0, requestOptions);
|
|
39843
|
-
const parsed = parseGitHubInstallIntentStart(raw, now());
|
|
39844
|
-
if (!parsed) throw new Error("server returned an invalid GitHub install-intent response");
|
|
39845
|
-
return parsed;
|
|
39846
|
-
}
|
|
39847
|
-
function defaultSleep4(milliseconds, signal) {
|
|
39848
|
-
if (signal.aborted) return Promise.reject(signal.reason);
|
|
39849
|
-
return new Promise((resolvePromise, reject) => {
|
|
39850
|
-
const onAbort = () => {
|
|
39851
|
-
clearTimeout(timer);
|
|
39852
|
-
reject(signal.reason);
|
|
39853
|
-
};
|
|
39854
|
-
const timer = setTimeout(() => {
|
|
39855
|
-
signal.removeEventListener("abort", onAbort);
|
|
39856
|
-
resolvePromise();
|
|
39857
|
-
}, milliseconds);
|
|
39858
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
39859
|
-
});
|
|
39860
|
-
}
|
|
39861
|
-
function expiredStatus(start) {
|
|
39862
|
-
return {
|
|
39863
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
39864
|
-
mode: MODE,
|
|
39865
|
-
found: true,
|
|
39866
|
-
status: "expired",
|
|
39867
|
-
expiresAt: start.expiresAt,
|
|
39868
|
-
closedAt: start.expiresAt
|
|
39869
|
-
};
|
|
39870
|
-
}
|
|
39871
|
-
async function pollGitHubInstallIntent(client, start, options = {}) {
|
|
39872
|
-
const now = options.now ?? Date.now;
|
|
39873
|
-
const sleep2 = options.sleep ?? defaultSleep4;
|
|
39874
|
-
const signal = options.signal ?? new AbortController().signal;
|
|
39875
|
-
const path9 = `${START_PATH}/${encodeURIComponent(start.intentId)}`;
|
|
39876
|
-
while (true) {
|
|
39877
|
-
if (signal.aborted) throw signal.reason;
|
|
39878
|
-
const remainingBeforeSleep = start.expiresAt - now();
|
|
39879
|
-
if (remainingBeforeSleep <= 0) return expiredStatus(start);
|
|
39880
|
-
await sleep2(Math.min(start.pollAfterMs, remainingBeforeSleep), signal);
|
|
39881
|
-
if (signal.aborted) throw signal.reason;
|
|
39882
|
-
const remainingBeforeRequest = start.expiresAt - now();
|
|
39883
|
-
if (remainingBeforeRequest <= 0) return expiredStatus(start);
|
|
39884
|
-
let raw;
|
|
39885
|
-
try {
|
|
39886
|
-
raw = await client.get(path9, {
|
|
39887
|
-
signal: AbortSignal.any([
|
|
39888
|
-
signal,
|
|
39889
|
-
AbortSignal.timeout(Math.min(POLL_REQUEST_TIMEOUT_MS2, remainingBeforeRequest))
|
|
39890
|
-
])
|
|
39891
|
-
});
|
|
39892
|
-
} catch (error) {
|
|
39893
|
-
if (signal.aborted) throw error;
|
|
39894
|
-
if (now() >= start.expiresAt) return expiredStatus(start);
|
|
39895
|
-
throw error;
|
|
39896
|
-
}
|
|
39897
|
-
const status = parseGitHubInstallIntentStatus(raw, start.expiresAt);
|
|
39898
|
-
if (!status) throw new Error("server returned an invalid GitHub install-intent status");
|
|
39899
|
-
if (status.status !== "pending" && status.status !== "claimed") return status;
|
|
39900
|
-
}
|
|
39901
|
-
}
|
|
39902
|
-
|
|
39903
|
-
// src/commands/github.ts
|
|
39904
|
-
var BIND_TIMEOUT_MS = 1e4;
|
|
39905
|
-
var START_TIMEOUT_MS = 1e4;
|
|
39906
|
-
var EXIT_OK3 = 0;
|
|
39907
|
-
var EXIT_FAILURE2 = 1;
|
|
39908
|
-
var EXIT_UNBOUND = 2;
|
|
39909
|
-
var defaultDependencies3 = {
|
|
39910
|
-
cwd: () => process.cwd(),
|
|
39911
|
-
gitToplevel,
|
|
39912
|
-
bindRepositoryWithClient,
|
|
39913
|
-
getPinnedClient,
|
|
39914
|
-
createInstallIntent: createGitHubInstallIntent,
|
|
39915
|
-
pollInstallIntent: pollGitHubInstallIntent,
|
|
39916
|
-
openBrowser,
|
|
39917
|
-
now: Date.now
|
|
39918
|
-
};
|
|
39919
|
-
function errorMessage(error) {
|
|
39920
|
-
return error instanceof Error ? error.message : String(error);
|
|
39921
|
-
}
|
|
39922
|
-
function reportConnectFailure(error) {
|
|
39923
|
-
const message = errorMessage(error);
|
|
39924
|
-
const human = boundedHealthError(message) ?? "unknown error";
|
|
39925
|
-
process.stderr.write(`[prim] github connect failed: ${human}
|
|
39926
|
-
`);
|
|
39927
|
-
printJson({
|
|
39928
|
-
status: "error",
|
|
39929
|
-
error: message,
|
|
39930
|
-
...error instanceof HttpError ? { httpStatus: error.status } : {}
|
|
39931
|
-
});
|
|
39932
|
-
process.exitCode = EXIT_FAILURE2;
|
|
39933
|
-
}
|
|
39934
|
-
function reportBinding(binding, installAttempted = false) {
|
|
39935
|
-
if (binding.status === "connected") {
|
|
39936
|
-
process.stderr.write(
|
|
39937
|
-
`[prim] repository binding connected for GitHub origin ${binding.repositoryFullName}
|
|
39938
|
-
`
|
|
39939
|
-
);
|
|
39940
|
-
printJson(binding);
|
|
39941
|
-
process.exitCode = EXIT_OK3;
|
|
39942
|
-
return;
|
|
39943
|
-
}
|
|
39944
|
-
process.stderr.write(
|
|
39945
|
-
installAttempted ? `[prim] GitHub installation completed, but ${binding.repositoryFullName} was not granted admin access; repository binding remains unbound
|
|
39946
|
-
` : `[prim] repository binding unbound for GitHub origin ${binding.repositoryFullName}; starting GitHub App installation
|
|
39947
|
-
`
|
|
39948
|
-
);
|
|
39949
|
-
printJson(binding);
|
|
39950
|
-
process.exitCode = EXIT_UNBOUND;
|
|
39951
|
-
}
|
|
39952
|
-
function terminalInstallError(status) {
|
|
39953
|
-
if (status.status === "failed_terminal") {
|
|
39954
|
-
return new Error(`GitHub installation failed: ${status.failureCode}`);
|
|
39955
|
-
}
|
|
39956
|
-
return new Error(`GitHub installation ${status.status}`);
|
|
39957
|
-
}
|
|
39958
|
-
async function completeInstallIntent(client, start, dependencies, browser) {
|
|
39959
|
-
process.stderr.write(`[prim] complete GitHub App installation at:
|
|
39960
|
-
${start.browserUrl}
|
|
39961
|
-
`);
|
|
39962
|
-
if (browser) dependencies.openBrowser(start.browserUrl);
|
|
39963
|
-
return dependencies.pollInstallIntent(client, start, {
|
|
39964
|
-
now: dependencies.now
|
|
39965
|
-
});
|
|
39966
|
-
}
|
|
39967
|
-
async function performGithubConnect(dependencies = defaultDependencies3, options = {}) {
|
|
39968
|
-
const root = dependencies.gitToplevel(dependencies.cwd());
|
|
39969
|
-
if (!root) {
|
|
39970
|
-
reportConnectFailure(
|
|
39971
|
-
new Error("not a git repository \u2014 run `prim github connect` inside a GitHub repository")
|
|
39972
|
-
);
|
|
39973
|
-
return;
|
|
39974
|
-
}
|
|
39975
|
-
try {
|
|
39976
|
-
const client = await dependencies.getPinnedClient({
|
|
39977
|
-
signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
|
|
39978
|
-
});
|
|
39979
|
-
const existing = await dependencies.bindRepositoryWithClient(root, client, {
|
|
39980
|
-
signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
|
|
39981
|
-
});
|
|
39982
|
-
if (existing.status === "connected") {
|
|
39983
|
-
reportBinding(existing);
|
|
39984
|
-
return;
|
|
39985
|
-
}
|
|
39986
|
-
const start = await dependencies.createInstallIntent(client, {
|
|
39987
|
-
signal: AbortSignal.timeout(START_TIMEOUT_MS),
|
|
39988
|
-
now: dependencies.now
|
|
39989
|
-
});
|
|
39990
|
-
const status = await completeInstallIntent(
|
|
39991
|
-
client,
|
|
39992
|
-
start,
|
|
39993
|
-
dependencies,
|
|
39994
|
-
options.browser ?? true
|
|
39995
|
-
);
|
|
39996
|
-
if (status.status !== "consumed") throw terminalInstallError(status);
|
|
39997
|
-
process.stderr.write(
|
|
39998
|
-
`[prim] GitHub installation verified: ${status.adminRepositoryCount} admin repositories (${status.repositoryCount} total)
|
|
39999
|
-
`
|
|
40000
|
-
);
|
|
40001
|
-
const binding = await dependencies.bindRepositoryWithClient(root, client, {
|
|
40002
|
-
signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
|
|
40003
|
-
});
|
|
40004
|
-
reportBinding(binding, true);
|
|
40005
|
-
} catch (error) {
|
|
40006
|
-
reportConnectFailure(error);
|
|
40007
|
-
}
|
|
40008
|
-
}
|
|
40009
|
-
function registerGithubCommands(program3, dependencies = defaultDependencies3) {
|
|
40010
|
-
const github = program3.command("github").description("Manage GitHub repository bindings");
|
|
40011
|
-
github.command("connect").description("Install or reuse the Primitive GitHub App and bind the current GitHub origin").option("--no-browser", "Print the GitHub installation URL without opening a browser").action(async (options, command) => {
|
|
40012
|
-
const globals = command.optsWithGlobals();
|
|
40013
|
-
await performGithubConnect(dependencies, {
|
|
40014
|
-
browser: options.browser && globals.nonInteractive !== true
|
|
40015
|
-
});
|
|
40016
|
-
});
|
|
40017
|
-
}
|
|
40018
|
-
|
|
40019
40057
|
// src/commands/hooks.ts
|
|
40020
40058
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
40021
40059
|
import {
|
|
@@ -40333,14 +40371,9 @@ function writeOwnHooks() {
|
|
|
40333
40371
|
mkdirSync11(PRIM_GIT_HOOKS_DIR, { recursive: true });
|
|
40334
40372
|
}
|
|
40335
40373
|
for (const spec of HOOKS) {
|
|
40336
|
-
|
|
40337
|
-
|
|
40338
|
-
|
|
40339
|
-
} else if (spec.hookName === POST_REWRITE.hookName) {
|
|
40340
|
-
writeFileSync8(path9, globalHookScript(spec), { mode: 493 });
|
|
40341
|
-
} else {
|
|
40342
|
-
writeFileSync8(path9, globalHookScript(spec), { mode: 493 });
|
|
40343
|
-
}
|
|
40374
|
+
writeFileSync8(resolve7(PRIM_GIT_HOOKS_DIR, spec.hookName), globalHookScript(spec), {
|
|
40375
|
+
mode: 493
|
|
40376
|
+
});
|
|
40344
40377
|
}
|
|
40345
40378
|
for (const name of PASSTHROUGH_HOOKS) {
|
|
40346
40379
|
writeFileSync8(resolve7(PRIM_GIT_HOOKS_DIR, name), passThroughScript(name), { mode: 493 });
|
|
@@ -41408,6 +41441,7 @@ function registerSetupCommand(program3, dependencies = {}) {
|
|
|
41408
41441
|
const agent = agentInput;
|
|
41409
41442
|
const scope = opts.scope;
|
|
41410
41443
|
const self = process.argv[1];
|
|
41444
|
+
const globals = program3.optsWithGlobals();
|
|
41411
41445
|
const run = dependencies.run ?? ((args, capture = false) => {
|
|
41412
41446
|
const r = spawnSync2(process.execPath, [self, ...args], {
|
|
41413
41447
|
stdio: capture ? ["inherit", "pipe", "ignore"] : "inherit",
|
|
@@ -41477,7 +41511,12 @@ function registerSetupCommand(program3, dependencies = {}) {
|
|
|
41477
41511
|
const enableStep = setupSteps.find((candidate) => candidate.key === "enable");
|
|
41478
41512
|
if (enableStep) {
|
|
41479
41513
|
note(`${enableStep.label} \xB7 installing\u2026`);
|
|
41480
|
-
const
|
|
41514
|
+
const enableArgs = [
|
|
41515
|
+
...enableStep.args,
|
|
41516
|
+
...globals.yes ? ["--yes"] : [],
|
|
41517
|
+
...globals.nonInteractive ? ["--non-interactive"] : []
|
|
41518
|
+
];
|
|
41519
|
+
const { code } = run(enableArgs);
|
|
41481
41520
|
results[enableStep.key] = code === 0 ? "ok" : enableStep.required ? "failed" : "skipped";
|
|
41482
41521
|
}
|
|
41483
41522
|
const healthStep = setupSteps.find((candidate) => candidate.key === "health");
|