jioogen 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +216 -70
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -363,9 +363,9 @@ var require_utils = __commonJS({
|
|
|
363
363
|
fpath.split(self.sep).forEach(function(name) {
|
|
364
364
|
if (!name || name.substr(-1, 1) === ":") return;
|
|
365
365
|
resolvedPath += self.sep + name;
|
|
366
|
-
var
|
|
366
|
+
var stat2;
|
|
367
367
|
try {
|
|
368
|
-
|
|
368
|
+
stat2 = self.fs.statSync(resolvedPath);
|
|
369
369
|
} catch (e) {
|
|
370
370
|
if (e.message && e.message.startsWith("ENOENT")) {
|
|
371
371
|
self.fs.mkdirSync(resolvedPath);
|
|
@@ -373,7 +373,7 @@ var require_utils = __commonJS({
|
|
|
373
373
|
throw e;
|
|
374
374
|
}
|
|
375
375
|
}
|
|
376
|
-
if (
|
|
376
|
+
if (stat2 && stat2.isFile()) throw Errors.FILE_IN_THE_WAY(`"${resolvedPath}"`);
|
|
377
377
|
});
|
|
378
378
|
}
|
|
379
379
|
mkdirSync(folder);
|
|
@@ -382,8 +382,8 @@ var require_utils = __commonJS({
|
|
|
382
382
|
const self = this;
|
|
383
383
|
if (self.fs.existsSync(path)) {
|
|
384
384
|
if (!overwrite) return false;
|
|
385
|
-
var
|
|
386
|
-
if (
|
|
385
|
+
var stat2 = self.fs.statSync(path);
|
|
386
|
+
if (stat2.isDirectory()) {
|
|
387
387
|
return false;
|
|
388
388
|
}
|
|
389
389
|
}
|
|
@@ -416,8 +416,8 @@ var require_utils = __commonJS({
|
|
|
416
416
|
const self = this;
|
|
417
417
|
self.fs.exists(path, function(exist) {
|
|
418
418
|
if (exist && !overwrite) return callback(false);
|
|
419
|
-
self.fs.stat(path, function(err,
|
|
420
|
-
if (exist &&
|
|
419
|
+
self.fs.stat(path, function(err, stat2) {
|
|
420
|
+
if (exist && stat2 && stat2.isDirectory()) {
|
|
421
421
|
return callback(false);
|
|
422
422
|
}
|
|
423
423
|
var folder = pth.dirname(path);
|
|
@@ -469,13 +469,13 @@ var require_utils = __commonJS({
|
|
|
469
469
|
for (const part of rel.split(pth.sep)) {
|
|
470
470
|
if (!part || part === ".") continue;
|
|
471
471
|
cur = pth.join(cur, part);
|
|
472
|
-
let
|
|
472
|
+
let stat2;
|
|
473
473
|
try {
|
|
474
|
-
|
|
474
|
+
stat2 = self.fs.lstatSync(cur);
|
|
475
475
|
} catch (e) {
|
|
476
476
|
break;
|
|
477
477
|
}
|
|
478
|
-
if (
|
|
478
|
+
if (stat2.isSymbolicLink()) throw Errors.FILE_IN_THE_WAY(`"${cur}"`);
|
|
479
479
|
}
|
|
480
480
|
};
|
|
481
481
|
Utils.prototype.findFiles = function(path) {
|
|
@@ -502,11 +502,11 @@ var require_utils = __commonJS({
|
|
|
502
502
|
self.fs.readdirSync(dir).forEach(function(file) {
|
|
503
503
|
const path2 = pth.join(dir, file);
|
|
504
504
|
if (escapesRoot(path2)) return;
|
|
505
|
-
const
|
|
505
|
+
const stat2 = self.fs.statSync(path2);
|
|
506
506
|
if (!pattern || pattern.test(path2)) {
|
|
507
|
-
files.push(pth.normalize(path2) + (
|
|
507
|
+
files.push(pth.normalize(path2) + (stat2.isDirectory() ? self.sep : ""));
|
|
508
508
|
}
|
|
509
|
-
if (
|
|
509
|
+
if (stat2.isDirectory() && recursive) {
|
|
510
510
|
const realDir = self.fs.realpathSync(path2);
|
|
511
511
|
if (!visited.has(realDir)) {
|
|
512
512
|
visited.add(realDir);
|
|
@@ -553,14 +553,14 @@ var require_utils = __commonJS({
|
|
|
553
553
|
if (!--pending) done();
|
|
554
554
|
return;
|
|
555
555
|
}
|
|
556
|
-
self.fs.stat(file, function(err3,
|
|
556
|
+
self.fs.stat(file, function(err3, stat2) {
|
|
557
557
|
if (err3) return done(err3);
|
|
558
|
-
if (!
|
|
558
|
+
if (!stat2) {
|
|
559
559
|
if (!--pending) done();
|
|
560
560
|
return;
|
|
561
561
|
}
|
|
562
|
-
results.push(pth.normalize(file) + (
|
|
563
|
-
if (!
|
|
562
|
+
results.push(pth.normalize(file) + (stat2.isDirectory() ? self.sep : ""));
|
|
563
|
+
if (!stat2.isDirectory()) {
|
|
564
564
|
if (!--pending) done();
|
|
565
565
|
return;
|
|
566
566
|
}
|
|
@@ -8433,8 +8433,11 @@ function envValue(name) {
|
|
|
8433
8433
|
const value = process.env[name]?.trim();
|
|
8434
8434
|
return value !== void 0 && value.length > 0 ? value : void 0;
|
|
8435
8435
|
}
|
|
8436
|
+
function configDirOverride() {
|
|
8437
|
+
return envValue(CONFIG_DIR_ENV) ?? envValue(LEGACY_CONFIG_DIR_ENV);
|
|
8438
|
+
}
|
|
8436
8439
|
function configDir() {
|
|
8437
|
-
const override =
|
|
8440
|
+
const override = configDirOverride();
|
|
8438
8441
|
return override !== void 0 ? resolve(override) : join2(homedir(), ".config", "jioogen");
|
|
8439
8442
|
}
|
|
8440
8443
|
function legacyConfigDir() {
|
|
@@ -8474,6 +8477,7 @@ async function readCredentialsFile(path) {
|
|
|
8474
8477
|
async function readCredentials() {
|
|
8475
8478
|
const primary = await readCredentialsFile(credentialsPath());
|
|
8476
8479
|
if (primary !== null) return primary;
|
|
8480
|
+
if (configDirOverride() !== void 0) return null;
|
|
8477
8481
|
const legacyPath = join2(legacyConfigDir(), CREDENTIALS_FILE);
|
|
8478
8482
|
if (legacyPath === credentialsPath()) return null;
|
|
8479
8483
|
return readCredentialsFile(legacyPath);
|
|
@@ -8558,6 +8562,10 @@ function isMissingFile(error) {
|
|
|
8558
8562
|
return isRecord(error) && error.code === "ENOENT";
|
|
8559
8563
|
}
|
|
8560
8564
|
|
|
8565
|
+
// src/errors.ts
|
|
8566
|
+
var UsageError = class extends Error {
|
|
8567
|
+
};
|
|
8568
|
+
|
|
8561
8569
|
// src/output.ts
|
|
8562
8570
|
function formatTable(headers, rows) {
|
|
8563
8571
|
const widths = headers.map(
|
|
@@ -8569,11 +8577,52 @@ function formatTable(headers, rows) {
|
|
|
8569
8577
|
}
|
|
8570
8578
|
|
|
8571
8579
|
// src/sidecar.ts
|
|
8572
|
-
import {
|
|
8580
|
+
import { randomUUID } from "node:crypto";
|
|
8581
|
+
import { mkdir as mkdir2, open, readFile as readFile3, rename, rm as rm2, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
8573
8582
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
8583
|
+
var LOCK_RETRY_MS = 25;
|
|
8584
|
+
var LOCK_TIMEOUT_MS = 3e3;
|
|
8585
|
+
var LOCK_STALE_MS = 1e4;
|
|
8574
8586
|
function sidecarPath(bundleDir) {
|
|
8575
8587
|
return join3(bundleDir, EXECUTION_SIDECAR_PATH);
|
|
8576
8588
|
}
|
|
8589
|
+
function lockPath(bundleDir) {
|
|
8590
|
+
return `${sidecarPath(bundleDir)}.lock`;
|
|
8591
|
+
}
|
|
8592
|
+
async function withStateLock(bundleDir, fn) {
|
|
8593
|
+
const target = lockPath(bundleDir);
|
|
8594
|
+
await mkdir2(dirname2(target), { recursive: true });
|
|
8595
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
8596
|
+
let handle = null;
|
|
8597
|
+
let staleRemoved = false;
|
|
8598
|
+
while (handle === null) {
|
|
8599
|
+
try {
|
|
8600
|
+
handle = await open(target, "wx");
|
|
8601
|
+
} catch (error) {
|
|
8602
|
+
if (!isLockBusy(error)) throw error;
|
|
8603
|
+
if (!staleRemoved && await removeStaleLock(target)) {
|
|
8604
|
+
staleRemoved = true;
|
|
8605
|
+
continue;
|
|
8606
|
+
}
|
|
8607
|
+
if (Date.now() >= deadline) {
|
|
8608
|
+
throw new Error(
|
|
8609
|
+
`Gagal mengambil lock sidecar setelah ${LOCK_TIMEOUT_MS / 1e3} detik: ${target}. Kemungkinan proses lain masih menulis; hapus file lock itu bila proses tersebut sudah mati.`
|
|
8610
|
+
);
|
|
8611
|
+
}
|
|
8612
|
+
await delay(LOCK_RETRY_MS);
|
|
8613
|
+
}
|
|
8614
|
+
}
|
|
8615
|
+
try {
|
|
8616
|
+
await handle.writeFile(`${process.pid}
|
|
8617
|
+
`, "utf8");
|
|
8618
|
+
return await fn();
|
|
8619
|
+
} finally {
|
|
8620
|
+
await handle.close().catch(() => {
|
|
8621
|
+
});
|
|
8622
|
+
await rm2(target, { force: true }).catch(() => {
|
|
8623
|
+
});
|
|
8624
|
+
}
|
|
8625
|
+
}
|
|
8577
8626
|
async function readState(bundleDir) {
|
|
8578
8627
|
let raw;
|
|
8579
8628
|
try {
|
|
@@ -8662,8 +8711,16 @@ async function writeState(bundleDir, state) {
|
|
|
8662
8711
|
const normalized = rebuild(state, state.tasks, state.current);
|
|
8663
8712
|
const target = sidecarPath(bundleDir);
|
|
8664
8713
|
await mkdir2(dirname2(target), { recursive: true });
|
|
8665
|
-
|
|
8714
|
+
const temp = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
8715
|
+
try {
|
|
8716
|
+
await writeFile2(temp, `${JSON.stringify(normalized, null, 2)}
|
|
8666
8717
|
`, "utf8");
|
|
8718
|
+
await rename(temp, target);
|
|
8719
|
+
} catch (error) {
|
|
8720
|
+
await rm2(temp, { force: true }).catch(() => {
|
|
8721
|
+
});
|
|
8722
|
+
throw error;
|
|
8723
|
+
}
|
|
8667
8724
|
return normalized;
|
|
8668
8725
|
}
|
|
8669
8726
|
function rebuild(state, tasks, current, now = /* @__PURE__ */ new Date()) {
|
|
@@ -8690,6 +8747,23 @@ function toTaskState(task) {
|
|
|
8690
8747
|
function isMissingFile2(error) {
|
|
8691
8748
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
8692
8749
|
}
|
|
8750
|
+
function isLockBusy(error) {
|
|
8751
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
8752
|
+
}
|
|
8753
|
+
async function removeStaleLock(target) {
|
|
8754
|
+
try {
|
|
8755
|
+
const info = await stat(target);
|
|
8756
|
+
if (Date.now() - info.mtimeMs < LOCK_STALE_MS) return false;
|
|
8757
|
+
await rm2(target, { force: true });
|
|
8758
|
+
return true;
|
|
8759
|
+
} catch (error) {
|
|
8760
|
+
if (isMissingFile2(error)) return true;
|
|
8761
|
+
throw error;
|
|
8762
|
+
}
|
|
8763
|
+
}
|
|
8764
|
+
function delay(ms) {
|
|
8765
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
8766
|
+
}
|
|
8693
8767
|
|
|
8694
8768
|
// src/skills.ts
|
|
8695
8769
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
@@ -9106,6 +9180,10 @@ async function extractBundle(bytes, targetDir) {
|
|
|
9106
9180
|
}
|
|
9107
9181
|
|
|
9108
9182
|
// src/commands.ts
|
|
9183
|
+
var PROJECT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9184
|
+
function isValidProjectId(projectId) {
|
|
9185
|
+
return PROJECT_ID_RE.test(projectId.trim());
|
|
9186
|
+
}
|
|
9109
9187
|
async function loginCommand(options) {
|
|
9110
9188
|
const token = options.token ?? envFirst("JIOOGEN_TOKEN", "JIOOCODE_TOKEN");
|
|
9111
9189
|
if (token !== void 0 && token.length > 0) {
|
|
@@ -9186,9 +9264,14 @@ async function projectsCommand(options) {
|
|
|
9186
9264
|
);
|
|
9187
9265
|
}
|
|
9188
9266
|
async function pullCommand(projectId, options) {
|
|
9267
|
+
if (!isValidProjectId(projectId)) {
|
|
9268
|
+
throw new UsageError(
|
|
9269
|
+
`Project id tidak valid: ${projectId}. Gunakan UUID dari \`jioogen projects\`.`
|
|
9270
|
+
);
|
|
9271
|
+
}
|
|
9189
9272
|
const { apiUrl, client } = await resolveApi(options);
|
|
9190
9273
|
const target = resolve4(options.dir ?? projectId);
|
|
9191
|
-
await assertPullTarget(target, projectId);
|
|
9274
|
+
await assertPullTarget(target, projectId, options.force === true);
|
|
9192
9275
|
const bytes = await client.getBytes(projectPath(projectId, "/bundle"));
|
|
9193
9276
|
const written = await extractBundle(bytes, target);
|
|
9194
9277
|
const skills = await installAgentSkills(target);
|
|
@@ -9264,9 +9347,15 @@ async function handoffCommand(options) {
|
|
|
9264
9347
|
}
|
|
9265
9348
|
}
|
|
9266
9349
|
async function taskNextCommand(options) {
|
|
9267
|
-
const {
|
|
9268
|
-
|
|
9269
|
-
|
|
9350
|
+
const { projectId, definitions, written, next } = await withLocalState(options, async (local) => {
|
|
9351
|
+
const written2 = await writeState(local.bundleDir, local.state);
|
|
9352
|
+
return {
|
|
9353
|
+
projectId: local.projectId,
|
|
9354
|
+
definitions: local.definitions,
|
|
9355
|
+
written: written2,
|
|
9356
|
+
next: selectNext(local.definitions, written2.tasks)
|
|
9357
|
+
};
|
|
9358
|
+
});
|
|
9270
9359
|
if (next === null) {
|
|
9271
9360
|
if (options.json === true) {
|
|
9272
9361
|
console.log(JSON.stringify({ done: true, projectId }, null, 2));
|
|
@@ -9315,62 +9404,71 @@ Status: ${next.state.status} \u2014 silakan ulangi.`);
|
|
|
9315
9404
|
Progress: ${written.counts?.done ?? 0}/${definitions.length} task selesai.`);
|
|
9316
9405
|
}
|
|
9317
9406
|
async function taskStartCommand(ref, options) {
|
|
9318
|
-
const
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9407
|
+
const task = await withLocalState(options, async (local) => {
|
|
9408
|
+
assertStartDependencies(ref, local.definitions, local.state.tasks);
|
|
9409
|
+
const updated = withStatus(local.state, ref, "in_progress", {
|
|
9410
|
+
awaitingCheckpoint: false,
|
|
9411
|
+
note: ""
|
|
9412
|
+
});
|
|
9413
|
+
await writeState(local.bundleDir, updated);
|
|
9414
|
+
return updated.tasks.find((entry) => entry.ref.toUpperCase() === ref.toUpperCase());
|
|
9322
9415
|
});
|
|
9323
|
-
await writeState(bundleDir, updated);
|
|
9324
|
-
const task = updated.tasks.find((entry) => entry.ref.toUpperCase() === ref.toUpperCase());
|
|
9325
9416
|
console.log(`Started ${task?.ref ?? ref} (attempt ${task?.attempt ?? 1}).`);
|
|
9326
9417
|
}
|
|
9327
9418
|
async function taskCompleteCommand(ref, options) {
|
|
9328
|
-
const {
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
const completedTask = completed.tasks.find(
|
|
9334
|
-
(entry) => entry.ref.toUpperCase() === ref.toUpperCase()
|
|
9335
|
-
);
|
|
9336
|
-
const next = selectNext(definitions, completed.tasks);
|
|
9337
|
-
if (completedTask !== void 0 && next !== null && isBoundary(completedTask, next.task)) {
|
|
9338
|
-
const withCheckpoint = setCurrent(completed, {
|
|
9339
|
-
ref: next.task.id,
|
|
9340
|
-
...next.task.layer !== void 0 ? { layer: next.task.layer } : {},
|
|
9341
|
-
phase: next.task.phase,
|
|
9342
|
-
awaitingCheckpoint: true
|
|
9419
|
+
const { completedTask, next, checkpoint } = await withLocalState(options, async (local) => {
|
|
9420
|
+
assertCompleteDependencies(ref, local.definitions, local.state.tasks, options.force === true);
|
|
9421
|
+
const completed = withStatus(local.state, ref, "done", {
|
|
9422
|
+
note: options.note ?? "",
|
|
9423
|
+
awaitingCheckpoint: false
|
|
9343
9424
|
});
|
|
9344
|
-
|
|
9345
|
-
|
|
9425
|
+
const completedTask2 = completed.tasks.find(
|
|
9426
|
+
(entry) => entry.ref.toUpperCase() === ref.toUpperCase()
|
|
9427
|
+
);
|
|
9428
|
+
const next2 = selectNext(local.definitions, completed.tasks);
|
|
9429
|
+
if (completedTask2 !== void 0 && next2 !== null && isBoundary(completedTask2, next2.task)) {
|
|
9430
|
+
const withCheckpoint = setCurrent(completed, {
|
|
9431
|
+
ref: next2.task.id,
|
|
9432
|
+
...next2.task.layer !== void 0 ? { layer: next2.task.layer } : {},
|
|
9433
|
+
phase: next2.task.phase,
|
|
9434
|
+
awaitingCheckpoint: true
|
|
9435
|
+
});
|
|
9436
|
+
await writeState(local.bundleDir, withCheckpoint);
|
|
9437
|
+
return { completedTask: completedTask2, next: next2, checkpoint: true };
|
|
9438
|
+
}
|
|
9439
|
+
await writeState(local.bundleDir, completed);
|
|
9440
|
+
return { completedTask: completedTask2, next: next2, checkpoint: false };
|
|
9441
|
+
});
|
|
9442
|
+
console.log(`Done ${completedTask?.ref ?? ref}.`);
|
|
9443
|
+
if (checkpoint && next !== null) {
|
|
9346
9444
|
console.log(
|
|
9347
9445
|
`Checkpoint berikutnya: ${next.task.id} (${next.task.layer ?? "-"} / phase ${next.task.phase}).`
|
|
9348
9446
|
);
|
|
9349
9447
|
console.log("Berhenti dan minta konfirmasi user sebelum task start berikutnya.");
|
|
9350
9448
|
return;
|
|
9351
9449
|
}
|
|
9352
|
-
await writeState(bundleDir, completed);
|
|
9353
|
-
console.log(`Done ${completedTask?.ref ?? ref}.`);
|
|
9354
9450
|
if (next !== null) console.log(`Berikutnya: ${next.task.id} \u2014 ${next.task.title}`);
|
|
9355
9451
|
}
|
|
9356
9452
|
async function taskFailCommand(ref, reasonParts, options) {
|
|
9357
9453
|
const reason = reasonParts.join(" ").trim() || options.note || "";
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9454
|
+
await withLocalState(options, async (local) => {
|
|
9455
|
+
const updated = withStatus(local.state, ref, "failed", {
|
|
9456
|
+
note: reason,
|
|
9457
|
+
awaitingCheckpoint: false
|
|
9458
|
+
});
|
|
9459
|
+
await writeState(local.bundleDir, updated);
|
|
9362
9460
|
});
|
|
9363
|
-
await writeState(bundleDir, updated);
|
|
9364
9461
|
console.log(`Failed ${ref}${reason.length > 0 ? `: ${reason}` : "."}`);
|
|
9365
9462
|
}
|
|
9366
9463
|
async function taskResetCommand(ref, reasonParts, options) {
|
|
9367
9464
|
const reason = reasonParts.join(" ").trim() || options.note || "";
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9465
|
+
await withLocalState(options, async (local) => {
|
|
9466
|
+
const updated = withStatus(local.state, ref, "todo", {
|
|
9467
|
+
note: reason,
|
|
9468
|
+
awaitingCheckpoint: false
|
|
9469
|
+
});
|
|
9470
|
+
await writeState(local.bundleDir, updated);
|
|
9372
9471
|
});
|
|
9373
|
-
await writeState(bundleDir, updated);
|
|
9374
9472
|
console.log(`Reset ${ref} ke todo${reason.length > 0 ? `: ${reason}` : "."}`);
|
|
9375
9473
|
}
|
|
9376
9474
|
async function statusCommand(options) {
|
|
@@ -9381,6 +9479,23 @@ async function statusCommand(options) {
|
|
|
9381
9479
|
const { bundleDir, projectId } = await resolveLocalBundle(options);
|
|
9382
9480
|
const existing = await readState(bundleDir);
|
|
9383
9481
|
if (existing === null) {
|
|
9482
|
+
if (options.json === true) {
|
|
9483
|
+
console.log(
|
|
9484
|
+
JSON.stringify(
|
|
9485
|
+
{
|
|
9486
|
+
projectId,
|
|
9487
|
+
hasExecution: false,
|
|
9488
|
+
tasks: [],
|
|
9489
|
+
counts: summarizeExecutionCounts([]),
|
|
9490
|
+
current: null,
|
|
9491
|
+
updatedAt: null
|
|
9492
|
+
},
|
|
9493
|
+
null,
|
|
9494
|
+
2
|
|
9495
|
+
)
|
|
9496
|
+
);
|
|
9497
|
+
return;
|
|
9498
|
+
}
|
|
9384
9499
|
console.log(`Project ${projectId}: belum ada sidecar. Jalankan \`jioogen task next\`.`);
|
|
9385
9500
|
return;
|
|
9386
9501
|
}
|
|
@@ -9487,14 +9602,40 @@ async function resolveBundle(options) {
|
|
|
9487
9602
|
const api = await resolveApi({ api: options.api ?? local.linkedApiUrl });
|
|
9488
9603
|
return { ...api, ...local };
|
|
9489
9604
|
}
|
|
9490
|
-
async function
|
|
9605
|
+
async function withLocalState(options, fn) {
|
|
9491
9606
|
const context = await resolveLocalBundle(options);
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9607
|
+
return withStateLock(context.bundleDir, async () => {
|
|
9608
|
+
const definitions = await loadTasks(context.bundleDir);
|
|
9609
|
+
const existing = await readState(context.bundleDir);
|
|
9610
|
+
const state = existing === null ? createState(context.projectId, definitions) : mergeTasks(existing, definitions);
|
|
9611
|
+
return fn({ ...context, definitions, state });
|
|
9612
|
+
});
|
|
9496
9613
|
}
|
|
9497
|
-
|
|
9614
|
+
function unmetDependencies(ref, definitions, states) {
|
|
9615
|
+
const key = ref.toUpperCase();
|
|
9616
|
+
const definition = definitions.find((task) => task.id.toUpperCase() === key);
|
|
9617
|
+
if (definition === void 0) return [];
|
|
9618
|
+
const byRef = new Map(states.map((state) => [state.ref.toUpperCase(), state]));
|
|
9619
|
+
return definition.dependsOn.filter(
|
|
9620
|
+
(dependency) => byRef.get(dependency.toUpperCase())?.status !== "done"
|
|
9621
|
+
);
|
|
9622
|
+
}
|
|
9623
|
+
function assertStartDependencies(ref, definitions, states) {
|
|
9624
|
+
const missing = unmetDependencies(ref, definitions, states);
|
|
9625
|
+
if (missing.length === 0) return;
|
|
9626
|
+
throw new Error(
|
|
9627
|
+
`Tidak bisa memulai ${ref}: dependency belum selesai (${missing.join(", ")}). Selesaikan dependency itu lebih dulu.`
|
|
9628
|
+
);
|
|
9629
|
+
}
|
|
9630
|
+
function assertCompleteDependencies(ref, definitions, states, force) {
|
|
9631
|
+
if (force) return;
|
|
9632
|
+
const missing = unmetDependencies(ref, definitions, states);
|
|
9633
|
+
if (missing.length === 0) return;
|
|
9634
|
+
throw new Error(
|
|
9635
|
+
`Tidak bisa menyelesaikan ${ref}: dependency belum selesai (${missing.join(", ")}). Selesaikan dependency itu lebih dulu, atau tambahkan --force bila memang disengaja.`
|
|
9636
|
+
);
|
|
9637
|
+
}
|
|
9638
|
+
async function assertPullTarget(target, projectId, force = false) {
|
|
9498
9639
|
let entries;
|
|
9499
9640
|
try {
|
|
9500
9641
|
entries = await readdir3(target);
|
|
@@ -9510,6 +9651,11 @@ async function assertPullTarget(target, projectId) {
|
|
|
9510
9651
|
if (link.projectId !== projectId) {
|
|
9511
9652
|
throw new Error(`Direktori ${target} terikat ke project ${link.projectId}.`);
|
|
9512
9653
|
}
|
|
9654
|
+
if (!force) {
|
|
9655
|
+
throw new Error(
|
|
9656
|
+
`Direktori ${target} sudah berisi bundle project ini; pull ulang akan menimpa edit lokal. Jalankan \`jioogen link ${projectId}\` bila hanya ingin mengikat, atau ulangi dengan --force.`
|
|
9657
|
+
);
|
|
9658
|
+
}
|
|
9513
9659
|
}
|
|
9514
9660
|
async function assertLinkTarget(target, projectId) {
|
|
9515
9661
|
if (!await hasBundleMarkers(target)) {
|
|
@@ -9605,7 +9751,7 @@ function isMissingFile4(error) {
|
|
|
9605
9751
|
}
|
|
9606
9752
|
|
|
9607
9753
|
// src/index.ts
|
|
9608
|
-
var VERSION = "0.2.
|
|
9754
|
+
var VERSION = "0.2.2";
|
|
9609
9755
|
var USAGE = `jioogen \u2014 CLI JiooGen (task selection lokal, server read-only)
|
|
9610
9756
|
|
|
9611
9757
|
Usage:
|
|
@@ -9614,14 +9760,14 @@ Usage:
|
|
|
9614
9760
|
jioogen logout
|
|
9615
9761
|
jioogen whoami
|
|
9616
9762
|
jioogen projects
|
|
9617
|
-
jioogen pull <projectId> [--dir <dir>]
|
|
9763
|
+
jioogen pull <projectId> [--dir <dir>] [--force]
|
|
9618
9764
|
jioogen link <projectId> [--dir <dir>]
|
|
9619
9765
|
jioogen skills install [--dir <dir>]
|
|
9620
9766
|
jioogen check [--dir <dir>] [--json]
|
|
9621
9767
|
jioogen handoff [--project <id>] [--json]
|
|
9622
9768
|
jioogen task next [--json]
|
|
9623
9769
|
jioogen task start <ref>
|
|
9624
|
-
jioogen task complete <ref> [--note <text>]
|
|
9770
|
+
jioogen task complete <ref> [--note <text>] [--force]
|
|
9625
9771
|
jioogen task fail <ref> [reason...] [--note <text>]
|
|
9626
9772
|
jioogen task reset <ref> [reason...] [--note <text>]
|
|
9627
9773
|
jioogen status [--remote] [--json]
|
|
@@ -9637,8 +9783,6 @@ Global options:
|
|
|
9637
9783
|
-h, --help Tampilkan bantuan
|
|
9638
9784
|
-v, --version Tampilkan versi
|
|
9639
9785
|
`;
|
|
9640
|
-
var UsageError = class extends Error {
|
|
9641
|
-
};
|
|
9642
9786
|
async function main() {
|
|
9643
9787
|
let raw = {};
|
|
9644
9788
|
let positionals = [];
|
|
@@ -9655,6 +9799,7 @@ async function main() {
|
|
|
9655
9799
|
project: { type: "string" },
|
|
9656
9800
|
dir: { type: "string" },
|
|
9657
9801
|
remote: { type: "boolean" },
|
|
9802
|
+
force: { type: "boolean" },
|
|
9658
9803
|
files: { type: "string" },
|
|
9659
9804
|
note: { type: "string" },
|
|
9660
9805
|
json: { type: "boolean" },
|
|
@@ -9680,6 +9825,7 @@ async function main() {
|
|
|
9680
9825
|
project: text(raw.project),
|
|
9681
9826
|
dir: text(raw.dir),
|
|
9682
9827
|
remote: flag(raw.remote),
|
|
9828
|
+
force: flag(raw.force),
|
|
9683
9829
|
files: text(raw.files),
|
|
9684
9830
|
note: text(raw.note),
|
|
9685
9831
|
json: flag(raw.json)
|