@telepath-computer/television 0.1.157 → 0.1.158

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/cli.cjs CHANGED
@@ -48121,6 +48121,16 @@ function hasTrailingSeparator(value) {
48121
48121
  const last = value.at(-1);
48122
48122
  return last === "/" || last === "\\";
48123
48123
  }
48124
+ function stripTrailingSeparators(value) {
48125
+ let end = value.length;
48126
+ while (end > 1 && (value[end - 1] === "/" || value[end - 1] === "\\")) {
48127
+ end -= 1;
48128
+ }
48129
+ if (end === 2 && end < value.length && /^[A-Za-z]:$/.test(value.slice(0, 2))) {
48130
+ end += 1;
48131
+ }
48132
+ return value.slice(0, end);
48133
+ }
48124
48134
  function artifactBasename(value) {
48125
48135
  const slash = value.lastIndexOf("/");
48126
48136
  const backslash = value.lastIndexOf("\\");
@@ -50374,8 +50384,7 @@ var ServerStore = class extends EventTarget {
50374
50384
  if (input.fields.url !== void 0) {
50375
50385
  throw new InvalidRequestError("artifact patch cannot set both path and url");
50376
50386
  }
50377
- const artifactPath = input.fields.path.trim();
50378
- this.validateArtifactPath(artifactPath);
50387
+ const artifactPath = this.normalizeArtifactPath(input.fields.path.trim());
50379
50388
  pointerChanged = artifactPath !== artifact.path;
50380
50389
  updated = { ...updated, path: artifactPath };
50381
50390
  }
@@ -50752,8 +50761,7 @@ var ServerStore = class extends EventTarget {
50752
50761
  if (input.url !== void 0) {
50753
50762
  throw new InvalidRequestError("path artifacts cannot set url");
50754
50763
  }
50755
- const artifactPath = input.path.trim();
50756
- this.validateArtifactPath(artifactPath);
50764
+ const artifactPath = this.normalizeArtifactPath(input.path.trim());
50757
50765
  return createArtifact({ ...input.id ? { id: input.id } : {}, kind: "path", title: input.title, path: artifactPath });
50758
50766
  }
50759
50767
  case "url": {
@@ -50771,32 +50779,40 @@ var ServerStore = class extends EventTarget {
50771
50779
  }
50772
50780
  }
50773
50781
  }
50774
- validateArtifactPath(artifactPath) {
50782
+ /**
50783
+ * Classify the supplied path by what is on disk and return the canonical
50784
+ * form to persist: directory paths always end with a trailing separator,
50785
+ * file paths never do. Callers may supply either form; the trailing
50786
+ * separator on the stored string is a server-maintained invariant that
50787
+ * routing (proxy, watcher, markdown gating, client dispatch) relies on
50788
+ * without statting.
50789
+ */
50790
+ normalizeArtifactPath(artifactPath) {
50775
50791
  if (!import_node_path11.default.isAbsolute(artifactPath)) {
50776
50792
  throw new InvalidRequestError("path must be absolute");
50777
50793
  }
50794
+ const stripped = stripTrailingSeparators(artifactPath);
50778
50795
  let stat;
50779
50796
  try {
50780
- (0, import_node_fs9.accessSync)(artifactPath, import_node_fs9.constants.R_OK);
50781
- stat = (0, import_node_fs9.statSync)(artifactPath);
50797
+ (0, import_node_fs9.accessSync)(stripped, import_node_fs9.constants.R_OK);
50798
+ stat = (0, import_node_fs9.statSync)(stripped);
50782
50799
  } catch {
50783
50800
  throw new InvalidRequestError(`path does not exist or is not readable: ${artifactPath}`);
50784
50801
  }
50785
- if (hasTrailingSeparator(artifactPath)) {
50786
- if (!stat.isDirectory()) {
50787
- throw new InvalidRequestError(`directory artifact path must resolve to a directory: ${artifactPath}`);
50802
+ if (stat.isDirectory()) {
50803
+ const canonical = hasTrailingSeparator(stripped) ? stripped : `${stripped}${import_node_path11.default.sep}`;
50804
+ if (!DIRECTORY_INDEX_BASENAMES.some((basename) => (0, import_node_fs9.existsSync)(import_node_path11.default.join(canonical, basename)))) {
50805
+ throw new InvalidRequestError(`directory artifact must contain index.html or index.htm: ${canonical}`);
50788
50806
  }
50789
- if (!DIRECTORY_INDEX_BASENAMES.some((basename) => (0, import_node_fs9.existsSync)(import_node_path11.default.join(artifactPath, basename)))) {
50790
- throw new InvalidRequestError(`directory artifact must contain index.html or index.htm: ${artifactPath}`);
50791
- }
50792
- return;
50807
+ return canonical;
50793
50808
  }
50794
50809
  if (!stat.isFile()) {
50795
- throw new InvalidRequestError(`file artifact path must resolve to a regular file: ${artifactPath}`);
50810
+ throw new InvalidRequestError(`path must resolve to a regular file or directory: ${artifactPath}`);
50796
50811
  }
50797
- if (!isAllowedArtifactFilePath(artifactPath)) {
50798
- throw new InvalidRequestError(`path extension must be one of .md, .markdown, .htm, .html: ${artifactPath}`);
50812
+ if (!isAllowedArtifactFilePath(stripped)) {
50813
+ throw new InvalidRequestError(`path extension must be one of .md, .markdown, .htm, .html: ${stripped}`);
50799
50814
  }
50815
+ return stripped;
50800
50816
  }
50801
50817
  canInstallOnboardingArtifact() {
50802
50818
  return !(0, import_node_fs9.existsSync)(getOnboardingArtifactSentinelPath(this.storagePath)) && !this._artifacts.has(ONBOARDING_ARTIFACT_ID) && this.onboardingArtifactSourcePath !== void 0;
@@ -51045,8 +51061,8 @@ function resolveVercelSkillsInstallerBin() {
51045
51061
  return localRequire.resolve("skills/bin/cli.mjs");
51046
51062
  }
51047
51063
  function readCLIVersion() {
51048
- if ("0.1.157".length > 0) {
51049
- return "0.1.157";
51064
+ if ("0.1.158".length > 0) {
51065
+ return "0.1.158";
51050
51066
  }
51051
51067
  const devPackageJsonPath = import_node_path12.default.join(getDevPackageDir(), "package.json");
51052
51068
  if (!(0, import_node_fs10.existsSync)(devPackageJsonPath)) {
@@ -51506,8 +51522,8 @@ function createProgram(env, argv = []) {
51506
51522
  });
51507
51523
  });
51508
51524
  program2.command("create-path-artifact").description(
51509
- "Create a path artifact that points at an existing absolute markdown or HTML file, or at a directory path ending in a separator with an index.html/index.htm. Television records the pointer only; HTML and directory targets are read-only, and markdown targets are written through only by in-UI editor saves via PUT /markdown/<id>."
51510
- ).requiredOption("--screen <id>", "Target screen ID").requiredOption("--title <title>", "Artifact title").requiredOption("--path <path>", "Absolute path to an existing markdown/HTML file or indexed directory").option("--focus-artifact", "Focus the new artifact after creation").option("--no-focus", "Create the artifact in the background without changing focus").option("--port <number>", "Server port", parsePortOption).option("--storage-path <path>", "Directory containing the Television token").addHelpText("after", buildArtifactWorkflowHelpNote(true)).action(async (opts) => {
51525
+ "Create a path artifact that points at an existing absolute markdown or HTML file, or at a directory with a root index.html/index.htm (trailing separator optional; the server detects file vs directory on disk). Television records the pointer only; HTML and directory targets are read-only, and markdown targets are written through only by in-UI editor saves via PUT /markdown/<id>."
51526
+ ).requiredOption("--screen <id>", "Target screen ID").requiredOption("--title <title>", "Artifact title").requiredOption("--path <path>", "Absolute path to an existing markdown/HTML file or indexed directory (trailing separator optional)").option("--focus-artifact", "Focus the new artifact after creation").option("--no-focus", "Create the artifact in the background without changing focus").option("--port <number>", "Server port", parsePortOption).option("--storage-path <path>", "Directory containing the Television token").addHelpText("after", buildArtifactWorkflowHelpNote(true)).action(async (opts) => {
51511
51527
  const shouldFocus = resolveFocusDirective(argv, "create-path-artifact", "--focus-artifact");
51512
51528
  const artifactPath = opts.path.trim();
51513
51529
  const client = createAuthenticatedClient(opts);
@@ -51544,10 +51560,21 @@ function createProgram(env, argv = []) {
51544
51560
  writeJSON(env.stdout, { storagePath: getTelevisionStoragePath() });
51545
51561
  });
51546
51562
  program2.command("update-artifact").description(
51547
- "Update artifact metadata in place. Currently only --title is changeable; ID, kind, path, and URL are immutable in this slice."
51548
- ).requiredOption("--id <id>", "Artifact ID").requiredOption("--title <title>", "New title").option("--port <number>", "Server port", parsePortOption).option("--storage-path <path>", "Directory containing the Television token").action(async (opts) => {
51563
+ "Update artifact metadata in place: --title on any artifact, --path to repoint a path artifact, --url to repoint a URL artifact. ID and kind are immutable. The server validates the new pointer; for path artifacts the target must exist (trailing separator optional) and the content watcher follows it."
51564
+ ).requiredOption("--id <id>", "Artifact ID").option("--title <title>", "New title").option("--path <path>", "New absolute path for a path artifact: markdown/HTML file or indexed directory (trailing separator optional)").option("--url <url>", "New http(s) URL for a URL artifact").option("--port <number>", "Server port", parsePortOption).option("--storage-path <path>", "Directory containing the Television token").action(async (opts) => {
51565
+ if (opts.title === void 0 && opts.path === void 0 && opts.url === void 0) {
51566
+ throw createDirectiveError("tv update-artifact requires at least one of --title, --path, or --url.");
51567
+ }
51568
+ if (opts.path !== void 0 && opts.url !== void 0) {
51569
+ throw createDirectiveError("tv update-artifact accepts --path or --url, not both; an artifact's kind is immutable.");
51570
+ }
51549
51571
  const client = createAuthenticatedClient(opts);
51550
- await client.artifacts.update({ artifactID: opts.id, title: opts.title });
51572
+ await client.artifacts.update({
51573
+ artifactID: opts.id,
51574
+ ...opts.title !== void 0 ? { title: opts.title } : {},
51575
+ ...opts.path !== void 0 ? { path: opts.path.trim() } : {},
51576
+ ...opts.url !== void 0 ? { url: opts.url.trim() } : {}
51577
+ });
51551
51578
  writeLine(env.stdout, `Artifact ${opts.id} updated.`);
51552
51579
  });
51553
51580
  program2.command("delete-artifact").description(
@@ -113,7 +113,7 @@ Commands group into four intents:
113
113
 
114
114
  - **Screen and display commands** — create, inspect, remove, or switch screens, or change the active display theme (`create-screen`, `list-screens`, `get-screen`, `remove-screen`, `focus-screen`, `focus-status`, `set-theme`).
115
115
  - **Artifact creation commands** — register path or URL artifacts (`create-path-artifact`, `create-url-artifact`).
116
- - **Artifact management commands** — inspect, retitle, focus, list, or delete existing artifacts (`delete-artifact`, `get-artifact`, `list-artifacts`, `update-artifact`, `focus-artifact`).
116
+ - **Artifact management commands** — inspect, retitle, repoint, focus, list, or delete existing artifacts (`delete-artifact`, `get-artifact`, `list-artifacts`, `update-artifact`, `focus-artifact`).
117
117
  - **Server and environment commands** — operate on the Television server itself (`serve`, `status`, `stop`, `storage-path`, `skills install`).
118
118
 
119
119
  When the CLI rejects a command, follow the directive it prints rather than guessing flags.
@@ -270,9 +270,9 @@ Always tell the user where you put the file.
270
270
 
271
271
  - **Single markdown file** ending in `.md` or `.markdown`
272
272
  - **Single HTML file** ending in `.htm` or `.html`
273
- - **HTML directory** — a path ending in `/` or `\` whose root contains `index.html` or `index.htm`, plus any sibling assets
273
+ - **HTML directory** — a directory whose root contains `index.html` or `index.htm`, plus any sibling assets
274
274
 
275
- The path must be absolute, existing, and readable by the server.
275
+ The path must be absolute, existing, and readable by the server. A trailing separator is optional — the server checks the path on disk to decide whether it is a file or a directory.
276
276
 
277
277
  ## Authoring quality
278
278
 
@@ -348,10 +348,10 @@ Single-file example:
348
348
  tv create-path-artifact --screen "<screen-id>" --title "Artifact title" --path /absolute/path/to/report.html --focus-artifact
349
349
  ```
350
350
 
351
- Directory example:
351
+ Directory example (trailing slash optional):
352
352
 
353
353
  ```bash
354
- tv create-path-artifact --screen "<screen-id>" --title "Artifact title" --path /absolute/path/to/dashboard/ --focus-artifact
354
+ tv create-path-artifact --screen "<screen-id>" --title "Artifact title" --path /absolute/path/to/dashboard --focus-artifact
355
355
  ```
356
356
 
357
357
  A directory artifact needs root `index.html` or `index.htm`. Keep sibling assets relative so they resolve through the artifact proxy:
@@ -401,6 +401,15 @@ To retitle an artifact:
401
401
  tv update-artifact --id "<artifact-id>" --title "New title"
402
402
  ```
403
403
 
404
+ To repoint an artifact at different content (same kind only — a path artifact takes `--path`, a URL artifact takes `--url`):
405
+
406
+ ```bash
407
+ tv update-artifact --id "<artifact-id>" --path /absolute/path/to/new-target
408
+ tv update-artifact --id "<artifact-id>" --url "https://example.com/next"
409
+ ```
410
+
411
+ The new path follows the same rules as creation (file or indexed directory, trailing separator optional). Rendering follows the new pointer immediately, and for path artifacts the content watcher retargets with it. Prefer repointing over delete-and-recreate when the artifact should keep its identity and screen placement.
412
+
404
413
  Artifact repositioning on its current screen is a browser UI drag/drop gesture; the CLI does not expose layout mutation today.
405
414
 
406
415
  To move the same underlying path or URL to another screen, delete the existing artifact and create a new one on the target screen with the same `--path` or `--url`.